Click here to Skip to main content
15,883,801 members
Articles / Programming Languages / C#

Null Object Design Pattern in Automated Testing

Rate me:
Please Sign up or sign in to vote.
5.00/5 (4 votes)
17 Apr 2016Ms-PL5 min read 12.4K   4   1
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

This article was originally posted at http://automatetheplanet.com/null-object-design-pattern

License

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


Written By
CEO Automate The Planet
Bulgaria Bulgaria
CTO and Co-founder of Automate The Planet Ltd, inventor of BELLATRIX Test Automation Framework, author of "Design Patterns for High-Quality Automated Tests: High-Quality Test Attributes and Best Practices" in C# and Java. Nowadays, he leads a team of passionate engineers helping companies succeed with their test automation. Additionally, he consults companies and leads automated testing trainings, writes books, and gives conference talks. You can find him on LinkedIn every day.

Comments and Discussions

 
PraiseGreat... 5 Pin
debashishPaul21-Apr-16 1:53
professionaldebashishPaul21-Apr-16 1:53 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.