Selenium tests, the Object Oriented Way

Some while ago i published an article in a local magazine on the topic of writing Selenium tests in an Object Oriented fashion.  As i believe this is a very useful way of reducing the number of asserts tests can include, and to expand to a bigger audience, here is a different approach to writing Selenium tests.

This approach is recommended when you have, for example, a big module whose properties you must compare to the expected ones, after performing an action or when it comes to testing the translation of the page.

For example, if you had a search engine that provides a list of results  when providing a search term, for each search that you performed the page would look the same but will display different results. Breaking the page down into objects and comparing objects rather than individual properties makes test maintenance and readability much easier.

Case Study

Let’s say the module you are trying to test is a shopping cart displayed after you select the items which you want to buy from your favorite site. Your shopping cart displays the relevant information regarding your purchased items, like their label, a description, an image, the price per product, the quantity in which the product is bought, the total price for each product type (which is the price per product multiplied by the quantity), and a button which allows a product to be removed from the shopping cart. Apart from the items you bought, the shopping cart also displays a label of its’ own, the total price of all the items that were bought, and a button that allows the user to proceed to the payment step. This page can also contain other information, like a module that suggests similar products to acquire, which would only display a small number of items, depicted by a label, an image and their price. shoppingCart

Such a shopping cart would be similar to the one depicted in the image on the right.

After adding all the desired products, the shopping cart is ready for testing (checking whether it displays the correct data – the products are the ones the user selected while shopping, the quantities match, the prices displayed are correct, and so on).

Usually the verification part of such a test would contain a large number of assertions. Even if these are placed in a separate method, they would be difficult to maintain and read. To avoid such an inconvenience, tests can be designed as being object oriented, as described below.

The solution
The analysis phase

First, you have to look at the big picture. What does the shopping cart represent? A collection of objects of different types. Analyzing the structure from complex to simple, one can notice that the most complex object (the one that encapsulates all the others), is the shopping cart itself. Its’ properties are: the label, the list of purchased items, a link, a button, and a side module. Out of these properties, the label can be represented in Java as a String, when it comes to the test. The price can also be represented by a String. The list of products is actually a list of objects of type Product. The Link is also an object, and so is the side module. Having analyzed the page top down, the ShoppingCart object can be represented as follows:

public class ShoppingCart {

private String title;

private List productList;

private String totalPrice;

private Button paymentButton;

private Link shopMoreLink;

private SuggestionsModule suggestionsModule;

}

Looking deeper into the identified structure, one can notice that: a product contains (has the following properties) – am image (which is another object), a label which represents its’ name (a String), a description (another String), the price per object (a String), the quantity (String), the total price for the acquired number of this type of product (String) and a button (this object is of type Button, mentioned already in the carts’ description). Having identified the product properties, the objectual representation can be as follows:

public class Product {

private String productLabel;

private String productDescription;

private Image image;

private String pricePerItem;

private String quantity;

private String totalPricePerProduct;

private Button removeButton;

}

The Link object mentioned in the carts’ definition can contain a minimal set of properties, which include a label (String) and the URL which opens when clicking it. Therefore, the Link object can be considered to be:

public class Link {

private String linkLabel;

private String linkURL;

}

By following this reasoning, one can identify all the properties of the objects that comprise the shopping cart. Therefore, these objects can be created by breaking each one down until the objects’ properties are primitives or Java objects.

Building the ‘expected’ content

After structuring the shopping cart, by breaking it down to objects, you need to understand how these will be used within tests. The first part of the test (or the part that will be performed before the test) will be to add the items to the shopping cart. The test will only need to check that the cart contains all the acquired products.

In order to build the expected shopping cart (the one you expect to see on the page after finishing the shopping part), a constructor must be written, which will assign to the objects’ properties values of the type those properties have. Therefore, the constructor will take a number of parameters that equals the number of the objects’ properties, each parameter having the type of the corresponding property type (for example, for a property of type String, a String parameter will be defined in the constructors’ signature).

Beginning from the easiest to build object, the constructors are written. For the Link object, we have the following constructor:

public Link(String linkLabel, String linkURL) {

this.linkLabel = linkLabel;

this.linkURL = linkURL;

}

Here – the Link object has a label and a URL, both of String type, whose values will be instantiated with the values retrieved from the constructors’ signature.

For the Product object, the following constructor will be written:

public Product(String productLabel, String productDescription, Image image, String pricePerItem, String

quantity, String totalPricePerProduct, Button removeButton) {

this.productLabel = productLabel;

this.productDescription = productDescription;

this.image = image;

this.pricePerItem = pricePerItem;

this.quantity = quantity;

this.totalPricePerProduct = totalPricePerProduct;

this.removeButton = removeButton;

}

The shopping carts’ constructor can be written as follows:

public ShoppingCart(String title, List productList, String totalPrice, Button paymentButton, Link

shopMoreLink, SuggestionsModule suggestionsModule) {

this.title = title;

this.productList = productList;

this.totalPrice = totalPrice;

this.paymentButton = paymentButton;

this.shopMoreLink = shopMoreLink;

this.suggestionsModule = suggestionsModule;

}

Based on the constructors, the following objects will be built, which will serve as the expected content:

  • the link for continuing to shop:
public static final Link CONTINUE_SHOPPING_LINK = new Link("Continue shopping",

"http://continue.shopping.com");

  • button for proceeding to the payment page:
public static final Button GO_TO_PAYMENT_BUTTON = new Button("Proceed to payment",

"http://some.url.com");

  • first product in the cart:
public static final Product LATTE_MACHIATTO_2 = new Product("Latte Machiato", "Classic latte

machiato with a dash of cocoa on top", Image.IMAGE_LATTE_MACHIATO, "5 EUR", "2", "10 EUR",

Button.REMOVE_PRODUCT);

  • here, the Image and Button objects have been using their own constructors
  • the second product from the cart:
public static final Product CHOCO_FRAPPE_3 = new Product("Choco-whip Frappe",

"Frappe with a twist of whipped cream and chocolate syrup",

Image.IMAGE_CHOCO_FRAPPE, "5 Eur", "3", "15 EUR", Button.REMOVE_PRODUCT);

  • here, the Image and Button objects were created by using their own constructors
  • the third product from the cart:
public static final Product CARAMEL_MOCCACHINO_1 = new Product("Caramel Moccachino", "Your

favourite moccachino with a refreshing taste of caramel", Image.IMAGE_CARAMEL_MOCCACHINO, "5

Eur", "1", "5 EUR", Button.REMOVE_PRODUCT);

  • here, the Image and Button objects were also generated from their own constructors
  • the expected shopping cart will be generated as follows:
public static final ShoppingCart SHOPPING_CART = new ShoppingCart("My Shopping

Cart",ImmutableList.of(Product.LATTE_MACHIATTO_2, Product.CHOCO_FRAPPE_3,

Product.CARAMEL_MOCCACHINO_1),"30 EUR", Button.GO_TO_PAYMENT_BUTTON,

Link.CONTINUE_SHOPPING_LINK, SuggestionsModule.SUGGESTIONS_MODULE);

  • here, the SuggestionModule object was generated with its’ own constructor, whereas the Product list, the Button and the Link were exemplified above.
Creating the ‘actual’ content

For building the ‘actual’ content, by reading the properties of the HTML elements on the web page, a new constructor will be written, which will have either a WebElement or a list of them as parameters, as many as are needed for instantiating the objects’ properties. WebElements are Selenium representations of HTML elements.

For example, for the Link object: the two properties, label and URL, can be extracted from a single WebElement. A Link element is represented in HTML as an <a> element, having a ‘href’ attribute (for identifying the URL). By calling Seleniums’ getText() method, you can acquire the value for the label property. Therefore, the constructor for generating the actual content can be written as follows, by passing in the <a> element that defines the Link:

public Link(WebElement element) {

this.linkLabel = element.getText();

this.linkURL = element.getAttribute("href");

}

Depending on how many WebElements are required for building an actual content of type Product, either one single WebElement will be used, or a list of them.  Assuming only one is required, the corresponding constructor will look like:

public Product(WebElement element) {

this.productLabel = element.findElement(By.cssSelector(someSelectorHere)).getText();

this.productDescription = element.findElement(By.cssSelector(someOtherSelectorHere)).getText();

this.image = new Image(element);

this.pricePerItem = element.findElement(By.cssSelector(anotherSelectorHere)).getText();

this.quantity = element.findElement(By.cssSelector(yetAnotherSelectorHere)).getText();

this.totalPricePerProduct = element.findElement(By.cssSelector(aSelectorHere)).getText();

this.removeButton = new Button(element);

}

One can notice that when it comes to the Product, for generating the values for each property, the constructor that builds the ‘actual’ content for each property type is called (constructors that also receive WebElements as parameters). Basically, any ‘actual’ constructor (based on WebElements) will only call ‘actual’ constructors for its’ ‘sub-objects’ (ones that also receive WebElements as parameters).

For the remaining properties – for example, for the productLabel, the getText() method from Selenium is called on an element that is relative to the element passed in the constructor.

Once all WebElement based constructors are defined, the most complex constructor, the one for the shopping cart, can also be built:

public ShoppingCart(List webElementList) {

this.title = webElementList.get(0).getText;

this.productList = productList;

this.totalPrice = webElementList.get(2).getText;

this.paymentButton = new Button(webElementList.get(3));

this.shopMoreLink = new Link(webElementList.get(4));

this.suggestionsModule = suggestionsModule;

}

The test

Once the objects and constructors have been built, the tests can be written. The requirement was to compare the shopping cart displayed on the web page to an expected one, by comparing each property of the two carts.

These properties are also objects, therefore their own properties must be compared to the properties of a set of expected objects. Since, in the first step, the ‘expected’ objects were built, after which the ‘actual’ ones were also created, the test can contain only one assert. It will compare all the properties by simply comparing the two ShoppingCart objects.

As a note, once an object is created, the equals() method must be implemented, for allowing the comparison of two instances of that given type.

Having said this, the test can be written as:

@Test

public void checkMyShoppingCart() {

assertEquals(new ShoppingCart(theListOfWebElements), SHOPPING_CART, “There was an

incorrect value În the shopping cart”);

}

This test does not cover the buying steps, where items are added to the cart, which should be done in a ‘before’ section of the test class.

In case the test needs to be run on several languages, a dataProvider can be passed to the test which contains the language for which the test will be run, and the expected translated content. In this case, the test becomes:

@Test(dataProvider = “theDataProvider”)

public void checkMyShoppingCart(String theLanguage, ShoppingCart

actualShoppingCartValuePerLanguage) {

changeTheLanguageOnTheSite(theLanguage);

assertEquals(new ShoppingCart(theListOfWebElements),

actualShoppingCartValuePerLanguage, “There was an incorrect value În the shopping cart”);

}

For this example, the dataProvider will look something like:

@DataProvider

public Object[][] theDataProvider() {

return new Object[][]{

{ “english”, SHOPPING_CART},

{ “spanish”, SHOPPING_CART_SPANISH}

};

}

Therefore, if the site would display let’s say about 20 languages, the test that will check for the translated content will have a small number of code lines, and will be written only once (but run several times).

Benefits of this approach

There are several benefits for writing tests in the object oriented manner. First, the test is a very short one, with a well defined goal, doing only one thing: comparing the ‘expected’ and ‘actual’ contents.

The test itself does not need a lot maintenance, since when the page changes, only the way of generating the page properties will need to be updated. Changing a label on the page requires only to change the expected value for that label, change which is isolated to only one line of code (which is used most probably in many tests).

Another benefit is given by the compact structure of the test, having no need for a large number of asserts, or for a large number of parameters for comparing their values. Instead of these parameters, the objects are compared directly.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s