Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Null Object Design Pattern in Automated Testing

5.00/5 (4 votes)
17 Apr 2016Ms-PL5 min read 12.9K  
An overview how to utilize the Null Object Design Pattern to simplify the automated tests' code and to make it less error prone.

If you are a regular reader of Automate The Planet you have most probably read some of my articles about Design Patterns in Automated Testing. The newest article from the famous series is dedicated to the Null Object Design Pattern. I am going to explain how to use the pattern to create a default behavior for your strategies and achieve a cleaner and more concise tests' code. Less branching in the code means lower complexity.

Image 1

Definition

<grammarly-btn>

In object-oriented computer programming, a Null Object is an object with no referenced value or with defined neutral ("null") behavior. The Null Object Design Pattern describes the uses of such objects and their behavior (or lack thereof).

  • Rid program logic of null checks where possible
  • Provide a non-functional object in place of a null reference
  • Allow methods to be called on Null objects, unlike a null reference
<grammarly-btn>

UML Class DiagramImage 2

<grammarly-btn>

Participants

The classes and objects participating in Null Object Design Pattern are:

  • IPurchasePromotionalCodeStrategy - Defines the interface for all strategies.
  • UiPurchasePromotionalCodeStrategy - The strategy that is responsible for applying and asserting promotional codes through the UI of the application.
  • NullPurchasePromotionalCodeStrategy - The null implementation of the strategy interface, provides the default implementation when no promotional code is applied.

Null Object Design Pattern C# Code

Test's Test Case

<grammarly-btn>

As in most of the examples from the series, we are going to automate a shopping cart process, the Amazon's one in particular. I am not going to explain the whole process, you can check the full explanations in my articles dedicated to the Strategy Design Pattern. The page that is interesting for us is the last page from the purchase process- Place Order Page. There you can apply promotional codes and gift cards.

<grammarly-btn>

Image 3

The main idea is that sometimes we need to add promotional codes and then assert that the correct amounts are displayed or saved in the DB. As you can assume there are various ways to accomplish that. One way is to use the UI directly and assert the text present in the labels. Another way is to use a direct access to the DB and insert the promotional code, then assert the calculated entries saved in some of the DB's tables. Also, you can achieve the same thing using a web service. I think you get the point- there are multiple solutions to the problem. In order to be able to understand fully the explanations below, I assume that you have read about the Strategy Design Pattern. If you haven't, I suggest you to do so. I think that one of the best solutions for the previously stated problem is to use the strategy design pattern

<grammarly-btn>

Image 4

The context holds a dependency to IStrategy and wraps the calls to the concrete strategies. In our case, the purchasing workflow is placed in the PurchaseContext class. We call the PurchaseItem method to perform a purchase. The PurchaseContext has a dependency to IPurchasePromotionalCodeStrategy. So depending on what we want to test we can  use the UI to apply and assert the promotional codes or use a direct DB access for a faster tests' execution.

<grammarly-btn>

IPurchasePromotionalCodeStrategy Interface

C#
public interface IPurchasePromotionalCodeStrategy
{
 void AssertPromotionalCodeDiscount();
 double GetPromotionalCodeDiscountAmount();
 void ApplyPromotionalCode(string couponCode);
}

The interface contains only three methods. One that applies the code, one to assert the code and the last one to get the discount's amount.

<grammarly-btn>

PurchaseContext Implementation without Null Object Design Pattern

<grammarly-btn>

This is how looks the PurchaseContext code if we don't use the Null Object Design Pattern.

C#
public class PurchaseContextNoNullObjects
{
 private readonly IPurchasePromotionalCodeStrategy purchasePromotionalCodeStrategy;
 private readonly ItemPage itemPage;
 private readonly PreviewShoppingCartPage previewShoppingCartPage;
 private readonly SignInPage signInPage;
 private readonly ShippingAddressPage shippingAddressPage;
 private readonly ShippingPaymentPage shippingPaymentPage;
 private readonly PlaceOrderPage placeOrderPage;
 public PurchaseContextNoNullObjects(
 IPurchasePromotionalCodeStrategy purchasePromotionalCodeStrategy,
 ItemPage itemPage,
 PreviewShoppingCartPage previewShoppingCartPage,
 SignInPage signInPage,
 ShippingAddressPage shippingAddressPage,
 ShippingPaymentPage shippingPaymentPage,
 PlaceOrderPage placeOrderPage)
 {
 this.purchasePromotionalCodeStrategy = purchasePromotionalCodeStrategy;
 this.itemPage = itemPage;
 this.previewShoppingCartPage = previewShoppingCartPage;
 this.signInPage = signInPage;
 this.shippingAddressPage = shippingAddressPage;
 this.shippingPaymentPage = shippingPaymentPage;
 this.placeOrderPage = placeOrderPage;
 }
 public void PurchaseItem(
 string itemUrl,
 string itemPrice,
 ClientLoginInfo clientLoginInfo,
 ClientPurchaseInfo clientPurchaseInfo)
 {
 this.itemPage.Navigate(itemUrl);
 this.itemPage.ClickBuyNowButton();
 this.previewShoppingCartPage.ClickProceedToCheckoutButton();
 this.signInPage.Login(clientLoginInfo.Email, clientLoginInfo.Password);
 this.shippingAddressPage.FillShippingInfo(clientPurchaseInfo);
 this.shippingAddressPage.ClickDifferentBillingCheckBox(clientPurchaseInfo);
 this.shippingAddressPage.ClickContinueButton();
 this.shippingPaymentPage.ClickBottomContinueButton();
 this.shippingAddressPage.FillBillingInfo(clientPurchaseInfo);
 this.shippingAddressPage.ClickContinueButton();
 this.shippingPaymentPage.ClickTopContinueButton();
 double couponDiscount = 0;
 if (purchasePromotionalCodeStrategy != null)
 {
 this.purchasePromotionalCodeStrategy.AssertPromotionalCodeDiscount();
 couponDiscount =
 this.purchasePromotionalCodeStrategy.GetPromotionalCodeDiscountAmount();
 }
 double totalPrice = double.Parse(itemPrice);
 this.placeOrderPage.AssertOrderTotalPrice(totalPrice, couponDiscount);
 // Some other actions...
 if (purchasePromotionalCodeStrategy != null)
 {
 this.purchasePromotionalCodeStrategy.AssertPromotionalCodeDiscount();
 }
 }
}

As I already have mentioned in the body of the PurchaseItem method you can find the order's completion workflow. Though a constructor injection, we pass all dependencies of the PurchaseContext such as all required pages and the concrete implementation of the IPurchasePromotionalCodeStrategy. However, there might be cases where we don't need to apply promotional codes and the strategy might no be initialized because of that we use null checks. If we don't use them a NullReferenceException might be thrown.

Image 5  

<grammarly-btn>

UI Implementation of IPurchasePromotionalCodeStrategy

This is how the UI implementation of the IPurchasePromotionalCodeStrategy interface looks.

<grammarly-btn>

<grammarly-btn>

C#
public class UiPurchasePromotionalCodeStrategy : IPurchasePromotionalCodeStrategy
{
 private readonly PlaceOrderPage placeOrderPage;
 private readonly double couponDiscountAmount;
 public UiPurchasePromotionalCodeStrategy(
 PlaceOrderPage placeOrderPage,
 double couponDiscountAmount)
 {
 this.placeOrderPage = placeOrderPage;
 this.couponDiscountAmount = couponDiscountAmount;
 }
 public void AssertPromotionalCodeDiscount()
 {
 Assert.AreEqual(
 this.couponDiscountAmount.ToString(),
 this.placeOrderPage.PromotionalDiscountPrice.Text);
 }
 public double GetPromotionalCodeDiscountAmount()
 {
 return this.couponDiscountAmount;
 }
 public void ApplyPromotionalCode(string couponCode)
 {
 this.placeOrderPage.PromotionalCode.SendKeys(couponCode);
 }
}

This concrete implementation of IPurchasePromotionalCodeStrategy is dependent to the PlaceOrderPage. We use the UI to apply and assert the promotional codes.

<grammarly-btn>

Null Object Implementation of IPurchasePromotionalCodeStrategy

The default implementation of the promotional codes' interface is pretty simple.

C#
public class NullPurchasePromotionalCodeStrategy : IPurchasePromotionalCodeStrategy
{
 public void AssertPromotionalCodeDiscount()
 {
 }
 public double GetPromotionalCodeDiscountAmount()
 {
 return 0;
 }
 public void ApplyPromotionalCode(string couponCode)
 {
 }
}

We return zero for the discount amount and the body of the rest of the methods is empty.

PurchaseContext Implementation with Null Object Design Pattern

As you have most probably noticed the code is almost identical to the previous implementation with the small difference that the null checks are missing.

C#
public class PurchaseContext
{
 private readonly IPurchasePromotionalCodeStrategy purchasePromotionalCodeStrategy;
 private readonly ItemPage itemPage;
 private readonly PreviewShoppingCartPage previewShoppingCartPage;
 private readonly SignInPage signInPage;
 private readonly ShippingAddressPage shippingAddressPage;
 private readonly ShippingPaymentPage shippingPaymentPage;
 private readonly PlaceOrderPage placeOrderPage;
 public PurchaseContext(
 IPurchasePromotionalCodeStrategy purchasePromotionalCodeStrategy,
 ItemPage itemPage,
 PreviewShoppingCartPage previewShoppingCartPage,
 SignInPage signInPage,
 ShippingAddressPage shippingAddressPage,
 ShippingPaymentPage shippingPaymentPage,
 PlaceOrderPage placeOrderPage)
 {
 this.purchasePromotionalCodeStrategy = purchasePromotionalCodeStrategy;
 this.itemPage = itemPage;
 this.previewShoppingCartPage = previewShoppingCartPage;
 this.signInPage = signInPage;
 this.shippingAddressPage = shippingAddressPage;
 this.shippingPaymentPage = shippingPaymentPage;
 this.placeOrderPage = placeOrderPage;
 }
 public void PurchaseItem(
 string itemUrl,
 string itemPrice,
 ClientLoginInfo clientLoginInfo,
 ClientPurchaseInfo clientPurchaseInfo)
 {
 this.itemPage.Navigate(itemUrl);
 this.itemPage.ClickBuyNowButton();
 this.previewShoppingCartPage.ClickProceedToCheckoutButton();
 this.signInPage.Login(clientLoginInfo.Email, clientLoginInfo.Password);
 this.shippingAddressPage.FillShippingInfo(clientPurchaseInfo);
 this.shippingAddressPage.ClickDifferentBillingCheckBox(clientPurchaseInfo);
 this.shippingAddressPage.ClickContinueButton();
 this.shippingPaymentPage.ClickBottomContinueButton();
 this.shippingAddressPage.FillBillingInfo(clientPurchaseInfo);
 this.shippingAddressPage.ClickContinueButton();
 this.shippingPaymentPage.ClickTopContinueButton();
 this.purchasePromotionalCodeStrategy.AssertPromotionalCodeDiscount();
 var couponDiscount =
 this.purchasePromotionalCodeStrategy.GetPromotionalCodeDiscountAmount();
 double totalPrice = double.Parse(itemPrice);
 this.placeOrderPage.AssertOrderTotalPrice(totalPrice, couponDiscount);
 this.purchasePromotionalCodeStrategy.AssertPromotionalCodeDiscount();
 }
}

Null Object Design Pattern is about giving a default implementation for filling the absence of an object and it is not about avoiding null reference exceptions. If you see Null Object Design Pattern implemented with no object collaboration then there is something wrong in the way the pattern is implemented.

<grammarly-btn>

<grammarly-btn>

Image 6

Null Object Design Pattern in Tests

The usage of the promotional code's strategies in tests is straightforward. If you want to apply a promotional code, you should use the UiPurchasePromotionalCodeStrategy or if you don't NullPurchasePromotionalCodeStrategy (where the default do-nothing behavior is wrapped). 

<grammarly-btn>

C#
[TestClass]
public class AmazonPurchaseNullObjectTests
{
 [TestInitialize]
 public void SetupTest()
 {
 Driver.StartBrowser();
 }
 [TestCleanup]
 public void TeardownTest()
 {
 Driver.StopBrowser();
 }
 [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(
 new NullPurchasePromotionalCodeStrategy(),
 new ItemPage(Driver.Browser),
 new PreviewShoppingCartPage(Driver.Browser),
 new SignInPage(Driver.Browser),
 new ShippingAddressPage(Driver.Browser),
 new ShippingPaymentPage(Driver.Browser),
 new PlaceOrderPage(Driver.Browser));
 ////var purchaseContext = new PurchaseContext(
 ////new UiPurchasePromotionalCodeStrategy(
 ////new PlaceOrderPage(Driver.Browser), 20),
 //// 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);
 }
}

In the next part of the series I am going to show you how to utilize the Null Object Design Pattern to the maximum extend through the Singleton Design Pattern and Unity IOC Container.

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

References

The post 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)