Click here to Skip to main content
13,898,214 members
Click here to Skip to main content
Add your own
alternative version

Tagged as

Stats

5.7K views
2 bookmarked
Posted 23 Apr 2016
Licenced Ms-PL

Advanced Null Object Design Pattern in Automated Testing

, 23 Apr 2016
Rate this:
Please Sign up or sign in to vote.
Improve your tests even further using the Null Object Design Pattern. Enhance your null objects through the usage of the Singleton Design Pattern or IoC container.

Introduction

This is the second article dedicated to the Null Object Design Pattern part of the Design Patterns in Automated Testing series. In the last post, I showed you how you can add a default behavior to your strategies. However, the usage of the strategies' context was tedious because its constructor accepted many dependencies that needed to be initialized manually. Here I will show you, how you can improve the usage of the Null Object Design Pattern through the Singleton Design Pattern and Unity IoC container.

Immutable Strategies via Base Singleton Implementation

If you have not already read my previous article about the Null Object Design Pattern, I suggest you to do so. The problem that I am going to solve here is the tedious initialization of the strategies' context. Another thing that can be improved is the performance. It makes sense to create just a single immutable instance of the NULL class. Because the NULL class just has default behavior, it makes more sense to cache it.

One way to do it is to create a base class that contains inside of it the Null object.

public abstract class BasePromotionalCodeStrategy : IPurchasePromotionalCodeStrategy
{
    public abstract void AssertPromotionalCodeDiscount();

    public abstract double GetPromotionalCodeDiscountAmount();

    public abstract void ApplyPromotionalCode(string couponCode);

    #region NULL

    static readonly NullPurchasePromotionalCodeStrategy nullPurchasePromotionalCodeStrategy =
    new NullPurchasePromotionalCodeStrategy();

    public static NullPurchasePromotionalCodeStrategy NULL
    {
        get
        {
            return nullPurchasePromotionalCodeStrategy;
        }
    }

    public class NullPurchasePromotionalCodeStrategy : BasePromotionalCodeStrategy
    {
        public override void AssertPromotionalCodeDiscount()
        {
        }

        public override double GetPromotionalCodeDiscountAmount()
        {
            return 0;
        }

        public override void ApplyPromotionalCode(string couponCode)
        {
        }
    }
        
    #endregion
}

All strategies derive from this base class.

public class UiPurchasePromotionalCodeStrategy : BasePromotionalCodeStrategy
{
    private readonly PlaceOrderPage placeOrderPage;
    private readonly double couponDiscountAmount;

    public UiPurchasePromotionalCodeStrategy(
        PlaceOrderPage placeOrderPage, 
        double couponDiscountAmount)
    {
        this.placeOrderPage = placeOrderPage;
        this.couponDiscountAmount = couponDiscountAmount;
    }

    public override void AssertPromotionalCodeDiscount()
    {
        Assert.AreEqual(
            this.couponDiscountAmount.ToString(),
            this.placeOrderPage.PromotionalDiscountPrice.Text);
    }

    public override double GetPromotionalCodeDiscountAmount()
    {
        return this.couponDiscountAmount;
    }

    public override void ApplyPromotionalCode(string couponCode)
    {
        this.placeOrderPage.PromotionalCode.SendKeys(couponCode);
    }
}

The difference with the previous implementation is that we do not have a separate file with the NullPurchasePromotionalCodeStrategy. The NULL property can be accessed by every child of the BasePromotionalCodeStrategy class. Below, you can find how one sample test will look.

[TestMethod]
public void Purchase_SeleniumTestingToolsCookbook()
{
    string itemUrl = "/Selenium-Testing-Cookbook-Gundecha-Unmesh/dp/1849515743";
    string itemPrice = "40.49";
    ClientPurchaseInfo clientPurchaseInfo = 
    new ClientPurchaseInfo(
        new ClientAddressInfo()
        {
            FullName = "John Smith",
            Country = "United States",
            Address1 = "950 Avenue of the Americas",
            State = "New York",
            City = "New York City",
            Zip = "10001-2121",
            Phone = "00164644885569"
        });
    clientPurchaseInfo.CouponCode = "99PERDIS";
    ClientLoginInfo clientLoginInfo = new ClientLoginInfo()
    {
        Email = "g3984159@trbvm.com",
        Password = "ASDFG_12345"
    };

    var purchaseContext = new PurchaseContext(
        UiPurchasePromotionalCodeStrategy.NULL,
        new ItemPage(Driver.Browser),
        new PreviewShoppingCartPage(Driver.Browser),
        new SignInPage(Driver.Browser),
        new ShippingAddressPage(Driver.Browser),
        new ShippingPaymentPage(Driver.Browser),
        new PlaceOrderPage(Driver.Browser));

    purchaseContext.PurchaseItem(
    itemUrl,
    itemPrice, 
    clientLoginInfo, 
    clientPurchaseInfo);
}

This is a convenient approach if you use the NULL object in more than one test because its instance will be reused among them.

Improve Null Object Design Pattern through Pure Singleton Design Pattern Implementation

In my opinion, nested classes or files containing more that one class is not a best practice. Because of that, I like more the pure implementation of the Singleton Design Pattern. Since most of the strategies need different parameters for every test, it doesn't make sense to be implemented as singletons. For the given examples, I think this is appropriate only for the NULL object.

For this implementation, we don't need the base class anymore, the singleton's code can be placed only in the NullPurchasePromotionalCodeStrategy class.

public class NullPurchasePromotionalCodeStrategy : IPurchasePromotionalCodeStrategy
{
    private static NullPurchasePromotionalCodeStrategy instance;

    public static NullPurchasePromotionalCodeStrategy NULL
    {
        get
        {
            if (instance == null)
            {
                instance = new NullPurchasePromotionalCodeStrategy();
            }
            return instance;
        }
    }

    public void AssertPromotionalCodeDiscount()
    {
    }

    public double GetPromotionalCodeDiscountAmount()
    {
        return 0;
    }

    public void ApplyPromotionalCode(string couponCode)
    {
    }
}

The usage in tests is identical to the previous approach, but the code is cleaner.

[TestMethod]
public void Purchase_SeleniumTestingToolsCookbook()
{
    string itemUrl = "/Selenium-Testing-Cookbook-Gundecha-Unmesh/dp/1849515743";
    string itemPrice = "40.49";
    ClientPurchaseInfo clientPurchaseInfo = new ClientPurchaseInfo(
        new ClientAddressInfo()
        {
            FullName = "John Smith",
            Country = "United States",
            Address1 = "950 Avenue of the Americas",
            State = "New York",
            City = "New York City",
            Zip = "10001-2121",
            Phone = "00164644885569"
        });
    clientPurchaseInfo.CouponCode = "99PERDIS";
    ClientLoginInfo clientLoginInfo = new ClientLoginInfo()
    {
        Email = "g3984159@trbvm.com",
        Password = "ASDFG_12345"
    };

    var purchaseContext = new PurchaseContext(NullPurchasePromotionalCodeStrategy.NULL,
        new ItemPage(Driver.Browser),
        new PreviewShoppingCartPage(Driver.Browser),
        new SignInPage(Driver.Browser),
        new ShippingAddressPage(Driver.Browser),
        new ShippingPaymentPage(Driver.Browser),
        new PlaceOrderPage(Driver.Browser));

    purchaseContext.PurchaseItem(itemUrl, itemPrice, clientLoginInfo, clientPurchaseInfo);
}

Improve Null Object Design Pattern Further through Unity IoC Container

As you can see from the last example, the initialization of the PurchaseContext is a real pain. If you have read my article about Unity IoC Container- Use IoC Container to Create Page Object Pattern on Steroids, you most probably have figured out what I meant with the title of this section. We can use almost all of the dependency objects as singletons since they don't have any state. Also, Unity can resolve all of them for us- recursively.

The first thing you need to do is to register all required types in the container.

private static IUnityContainer container = new UnityContainer();

[TestInitialize]
public void SetupTest()
{
    Driver.StartBrowser();
    container.RegisterType<ItemPage>(new ContainerControlledLifetimeManager());
    container.RegisterType<PreviewShoppingCartPage>(new ContainerControlledLifetimeManager());
    container.RegisterType<SignInPage>(new ContainerControlledLifetimeManager());
    container.RegisterType<ShippingAddressPage>(new ContainerControlledLifetimeManager());
    container.RegisterType<ShippingPaymentPage>(new ContainerControlledLifetimeManager());
    container.RegisterType<PlaceOrderPage>(new ContainerControlledLifetimeManager());
    container.RegisterType<PurchaseContext>(new ContainerControlledLifetimeManager());
    container.RegisterType<
    IPurchasePromotionalCodeStrategy, 
    NullPurchasePromotionalCodeStrategy>(new ContainerControlledLifetimeManager());
    container.RegisterInstance<IWebDriver>(Driver.Browser);
}

When you pass an instance of ContainerControlledLifetimeManager to the RegisterType method, you configure Unity to resolve your type as a singleton. All pages have only a single implementation and no interface, because of that we register them directly. This is not valid for the promotional code's strategies. Therefore, we tell Unity to resolve the IPurchasePromotionalCodeStrategy as our Null Object Design Pattern implementation by default. Through the last line of the method, we register the already created instance of WebDriver. Most of this code can be moved to the AssemblyInitialize method of our test project.

Resolve Null Object through Unity IoC Container

[TestMethod]
public void Purchase_SeleniumTestingToolsCookbook()
{
    string itemUrl = "/Selenium-Testing-Cookbook-Gundecha-Unmesh/dp/1849515743";
    string itemPrice = "40.49";
    ClientPurchaseInfo clientPurchaseInfo = new ClientPurchaseInfo(
        new ClientAddressInfo()
        {
            FullName = "John Smith",
            Country = "United States",
            Address1 = "950 Avenue of the Americas",
            State = "New York",
            City = "New York City",
            Zip = "10001-2121",
            Phone = "00164644885569"
        });
    clientPurchaseInfo.CouponCode = "99PERDIS";
    ClientLoginInfo clientLoginInfo = new ClientLoginInfo()
    {
        Email = "g3984159@trbvm.com",
        Password = "ASDFG_12345"
    };
    var purchaseContext = container.Resolve<PurchaseContext>();
    purchaseContext.PurchaseItem(itemUrl, itemPrice, clientLoginInfo, clientPurchaseInfo);
}

As you can see, the code of the test is shorter and more readable thanks to the Unity container. Moreover, all pages and all other dependencies will be reused in the rest of the tests.

Resolve Non-Null Object through Unity IoC Container

You can override the default strategy implementation for the promotional codes.

[TestMethod]
public void Purchase_SeleniumTestingToolsCookbook()
{
    string itemUrl = "/Selenium-Testing-Cookbook-Gundecha-Unmesh/dp/1849515743";
    string itemPrice = "40.49";
    ClientPurchaseInfo clientPurchaseInfo = new ClientPurchaseInfo(
        new ClientAddressInfo()
        {
            FullName = "John Smith",
            Country = "United States",
            Address1 = "950 Avenue of the Americas",
            State = "New York",
            City = "New York City",
            Zip = "10001-2121",
            Phone = "00164644885569"
        });
    clientPurchaseInfo.CouponCode = "99PERDIS";
    ClientLoginInfo clientLoginInfo = new ClientLoginInfo()
    {
        Email = "g3984159@trbvm.com",
        Password = "ASDFG_12345"
    };
    container.RegisterInstance<IPurchasePromotionalCodeStrategy>(
        new UiPurchasePromotionalCodeStrategy(container.Resolve<PlaceOrderPage>(), 40.49));
    var purchaseContext = container.Resolve<PurchaseContext>();
    purchaseContext.PurchaseItem(itemUrl, itemPrice, clientLoginInfo, clientPurchaseInfo);
}

Now if you run the test, you will notice that the UiPurchasePromotionalCodeStrategy class will be used in the strategies' context.

So Far in the "Design Patterns in Automated Testing" Series

  1. Page Object Pattern
  2. Advanced Page Object Pattern
  3. Facade Design Pattern
  4. Singleton Design Pattern
  5. Fluent Page Object Pattern
  6. IoC Container and Page Objects
  7. Strategy Design Pattern
  8. Advanced Strategy Design Pattern
  9. Observer Design Pattern
  10. Observer Design Pattern via Events and Delegates
  11. Observer Design Pattern via IObservable and IObserver
  12. Decorator Design Pattern- Mixing Strategies
  13. Page Objects That Make Code More Maintainable
  14. Improved Facade Design Pattern in Automation Testing v.2.0
  15. Rules Design Pattern
  16. Specification Design Pattern
  17. Advanced Specification Design Pattern
  18. Null Object Design Pattern

If you enjoy my publications, feel free to SUBSCRIBE
Also, hit these share buttons. Thank you!

The post Advanced Null Object Design Pattern in Automated Testing appeared first on Automate The Planet.

All images are purchased from DepositPhotos.com and cannot be downloaded and used for free.
License Agreement

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)

Share

About the Author

Anton Angelov
CEO Automate The Planet
Bulgaria Bulgaria
Anton Angelov is an IT Consultant and Quality Assurance Architect at Innovative Lab. He is passionate about automation testing and designing test harness and tools, having the best industry development practices in mind. In addition, he is an active blogger and the founder of Automate The Planet. He strives to make the site one of the leading authorities in Automation Testing by presenting compelling articles, inspiring ardent discussions amongst the community. He is also one of the most-rated-answer authors of questions about Test Automation Frameworks (WebDriver) on Stack Overflow.

You may also be interested in...

Pro

Comments and Discussions

 
-- There are no messages in this forum --
Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web02 | 2.8.190306.1 | Last Updated 24 Apr 2016
Article Copyright 2016 by Anton Angelov
Everything else Copyright © CodeProject, 1999-2019
Layout: fixed | fluid