Achieving high-quality test automation that brings value- you need to understand core programming concepts such as SOLID and the usage of design patterns. In the previous article, we investigated the Adapter design pattern and how it can help us to eliminate the usage of hard-coded pauses in our automated tests and instead automatically wait for elements to appear. Moreover, it helped us to improve the API Usability of our tests.
In this publication, we will investigate how we can extend the Adapter design pattern through the Composite design pattern so that our adapters to include the same style assertions. The style assertions are verification methods for checking the styles of the web elements against our style sheet requirements.
For more detailed overview and usage of many more design patterns and best practices in automated testing, check my book “Design Patterns for High-Quality Automated Tests, C# Edition, High-Quality Tests Attributes, and Best Practices”. You can read part of three of the chapters:
Defining High-Quality Test Attributes for Automated Tests
Benchmarking for Assessing Automated Test Components Performance
Generic Repository Design Pattern- Test Data Preparation
Overview Video
The Problem
We will start by checking the requirements of what we need to automate. Imagine that you work for a startup called “EU Space Rockets”. Our company makes the world a better place by allowing people to buy rockets through our website. How cool is that! Your job is to create a series of automated tests and make sure that everything is working as expected.
Our website uses modern web technologies and all actions are loading asynchronous instead of reloading the whole page.

We received the requirement that all buttons on the website should follow the same styles. For example- their font size to be 14px, and the font-weight to be equal to 600. We need to create tests for all other visual elements on the website, such as images, links, text fields, and so on. The solution should work for a single element and as well as for a collection of elements.
Quick Recall- Adapter Design Pattern Implementation
How about quickly recall what our decorators looked like?
public class DriverAdapter : IDriver
{
private readonly IWebDriver _driver;
public DriverAdapter(IWebDriver driver)
{
_driver = driver;
}
public void GoToUrl(string url)
{
_driver.Navigate().GoToUrl(url);
}
public Uri Url
{
get => new Uri(_driver.Url);
set => _driver.Url = value.ToString();
}
public IElement Create(By locator)
{
return new ElementAdapter(_driver, locator);
}
public IElementsList CreateElements(By locator)
{
return new ElementsList(_driver, locator);
}
public void WaitForAjax()
{
var timeout = TimeSpan.FromSeconds(30);
var sleepInterval = TimeSpan.FromSeconds(2);
var webDriverWait = new WebDriverWait(new SystemClock(), _driver, timeout, sleepInterval);
var js = (IJavaScriptExecutor)_driver;
webDriverWait.Until(wd => js.ExecuteScript("return jQuery.active").ToString() == "0");
}
public void Close()
{
_driver.Quit();
_driver.Dispose();
}
}
The DriverAdapter implements a simpler and enhanced IDriver interface. It wraps the usage of the Adaptee (IWebDriver) through the Composition Principle. It creates element adapters which are found later on when needed. We used the Lazy Loading design pattern to achieve this behavior. Also, it gives us the new WaitForAjax method.
Here is the implementation of the ElementDriver:
public class ElementAdapter : IElement
{
private readonly IWebDriver _driver;
private readonly ElementFinderService _elementFinder;
public ElementAdapter(IWebDriver driver, By by)
{
_driver = driver;
By = by;
_elementFinder = new ElementFinderService(driver);
}
public IWebElement NativeWebElement
{
get => _elementFinder.Find(By);
}
public By By
{
get;
}
public string Text => NativeWebElement?.Text;
public bool? Enabled => NativeWebElement?.Enabled;
public bool? Displayed => NativeWebElement?.Displayed;
public void Click()
{
WaitToBeClickable(By);
NativeWebElement?.Click();
}
public IElement CreateElement(By locator)
{
return new ElementAdapter(_driver, locator);
}
public IElementsList CreateElements(By locator)
{
return new ElementsList(_driver, locator);
}
public void TypeText(string text)
{
var webElement = NativeWebElement;
webElement?.Clear();
webElement?.SendKeys(text);
}
private void WaitToBeClickable(By by)
{
var webDriverWait = new WebDriverWait(_driver, TimeSpan.FromSeconds(30));
webDriverWait.Until(SeleniumExtras.WaitHelpers.ExpectedConditions.ElementToBeClickable(by));
}
}
Also, to achieve the lazy loading, we created a collection for the new adapter elements called ElementList.
public class ElementsList : IElementsList
{
private readonly By _by;
private readonly ElementFinderService _elementFinder;
private readonly IWebDriver _driver;
public ElementsList(IWebDriver driver, By by)
{
_by = by;
_elementFinder = new ElementFinderService(driver);
_driver = driver;
}
public IElement this => GetAndWaitWebDriverElements().ElementAt(i);
public IEnumerator<IElement> GetEnumerator() => GetAndWaitWebDriverElements().GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public int Count()
{
return _elementFinder.FindAll(_by).Count();
}
public void ForEach(Action<IElement> action)
{
foreach (var element in this)
{
action(element);
}
}
private IEnumerable<IElement> GetAndWaitWebDriverElements()
{
var nativeElements = _elementFinder.FindAll(_by);
foreach (var nativeElement in nativeElements)
{
IElement element = new ElementAdapter(_driver, _by);
yield
return element;
}
}
}
Style Assertions
As mentioned, the style assertions are verification methods for checking the styles of the web elements against our style sheet requirements. We can add the following methods to the ElementDecorator class.
public void AssertBackgroundColor(string expectedBackgroundColor)
{
Assert.AreEqual(expectedBackgroundColor, NativeWebElement.GetCssValue("background-color"));
}
public void AssertBorderColor(string expectedBorderColor)
{
Assert.AreEqual(expectedBorderColor, NativeWebElement.GetCssValue("border-color"));
}
public void AssertColor(string expectedColor)
{
Assert.AreEqual(expectedColor, NativeWebElement.GetCssValue("color"));
}
public void AssertFontFamily(string expectedFontFamily)
{
Assert.AreEqual(expectedFontFamily, NativeWebElement.GetCssValue("font-family"));
}
public void AssertFontWeight(string expectedFontWeight)
{
Assert.AreEqual(expectedFontWeight, NativeWebElement.GetCssValue("font-weight"));
}
public void AssertFontSize(string expectedFontSize)
{
Assert.AreEqual(expectedFontSize, NativeWebElement.GetCssValue("font-size"));
}
public void AssertTextAlign(string expectedTextAlign)
{
Assert.AreEqual(expectedTextAlign, NativeWebElement.GetCssValue("text-align"));
}
public void AssertVerticalAlign(string expectedVerticalAlign)
{
Assert.AreEqual(expectedVerticalAlign, NativeWebElement.GetCssValue("vertical-align"));
}
This is how we can use them for a single web element.
public void VerifyStylesOfAddToCartButton()
{
_driver.GoToUrl("http://demos.bellatrix.solutions/");
var falcon0AddToCartButton = _driver.Create(By.CssSelector("[data-product_id*='28']"));
falcon0AddToCartButton.AssertFontSize("14px");
falcon0AddToCartButton.AssertFontWeight("600");
}
Let’s review two ways how you can execute the same style assertions for a collection of elements. Here is the first way:
public void VerifyStylesOfAddToCartButtons()
{
_driver.GoToUrl("http://demos.bellatrix.solutions/");
var addToCartButtons = _driver.CreateElements(By.XPath("//a[contains(text(),'Add to cart')]"));
foreach (var addToCartButton in addToCartButtons)
{
addToCartButton.AssertFontSize("14px");
addToCartButton.AssertFontWeight("600");
}
}
Another way of doing the same thing but in a bit shorter manner is to use the ElementList ForEach method.
public void VerifyStylesOfAddToCartButtons()
{
_driver.GoToUrl("http://demos.bellatrix.solutions/");
var addToCartButtons = _driver.CreateElements(By.XPath("//a[contains(text(),'Add to cart')]"));
addToCartButtons.ForEach(e => e.AssertFontSize("14px"));
addToCartButtons.ForEach(e => e.AssertFontWeight("600"));
}
Anyhow, I believe the shorter version to be again a bit verbose because of the lambda expression. Won’t it be cool to be able to use the same syntax for a single element for the collection too? Something like this:
public void VerifyStylesOfAddToCartButtons()
{
_driver.GoToUrl("http://demos.bellatrix.solutions/");
var addToCartButtons = _driver.CreateElements(By.XPath("//a[contains(text(),'Add to cart')]"));
addToCartButtons.AssertFontSize("14px");
addToCartButtons.AssertFontWeight("600");
}
We can achieve this syntax if we implement the Composite design pattern. Let’s review how to do it.
Composite Design Pattern
Definition
Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly.
If I need to rephrase it- we can execute the same actions against a single item and the whole collection. If we are in the context of the collection, the action will be performed against all elements.
UML Class Diagram
classDiagram
IStyleAssertedElement <|.. IElement
IStyleAssertedElement <|.. IElementList
IElement <|.. ElementAdapter
IElementList <|.. ElementList
ElementList o-- IElement
Tests --> IStyleAssertedElement
class IStyleAssertedElement {
<<interface>>
+AssertBackgroundColor(string color)
+AssertBorderColor(string color)
+AssertColor(string color)
+AssertFontFamily(string fontFamily)
+AssertFontWeight(string fontWeight)
+AssertFontSize(string fontSize)
+AssertTextAlign(string textAlign)
+AssertVerticalAlign(string verticalAlign)
}
class IElement {
<<interface>>
}
class IElementList {
<<interface>>
}
class ElementAdapter {
}
class ElementList {
}
class Tests {
}
Participants
-
Component (IStyleAssertedElement)
The interface for objects in the composition. Defines methods for accessing and managing its child components.
-
Composite (IElementList, ElementList)
Defines behavior for components having children. Stores child components. Implements child-related operations in the Component interface.
-
Leaf (IElement, ElementAdapter)
Represents the lead object in the composition. A leaf has no children. Defines behavior for primitive objects in the composition.
-
Client (Tests)
Collaborates with objects conforming to the Component interface.
Composite Design Pattern Implementation
Let’s first create the shared Component interface holding the style assertions. The IElement and IElementList interfaces will inherit it.
public interface IStyleAssertedElement
{
void AssertBackgroundColor(string expectedBackgroundColor);
void AssertBorderColor(string expectedBorderColor);
void AssertColor(string expectedColor);
void AssertFontFamily(string expectedFontFamily);
void AssertFontWeight(string expectedFontWeight);
void AssertFontSize(string expectedFontSize);
void AssertTextAlign(string expectedTextAlign);
void AssertVerticalAlign(string expectedVerticalAlign);
}
We don’t need to change anything in the ElementDecorator. The definitions of the style assertions will stay the same. The only difference we need to make is that we need to implement these methods in the ElementList “Composite” class.
public class ElementsList : IElementsList
{
private readonly By _by;
private readonly ElementFinderService _elementFinder;
private readonly IWebDriver _driver;
public ElementsList(IWebDriver driver, By by)
{
_by = by;
_elementFinder = new ElementFinderService(driver);
_driver = driver;
}
public IElement this => GetAndWaitWebDriverElements().ElementAt(i);
public IEnumerator<IElement> GetEnumerator() => GetAndWaitWebDriverElements().GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public int Count()
{
return _elementFinder.FindAll(_by).Count();
}
public void ForEach(Action<IElement> action)
{
foreach (var element in this)
{
action(element);
}
}
public void AssertBackgroundColor(string expectedBackgroundColor)
{
ForEach(e => e.AssertBackgroundColor(expectedBackgroundColor));
}
public void AssertBorderColor(string expectedBorderColor)
{
ForEach(e => e.AssertBorderColor(expectedBorderColor));
}
public void AssertColor(string expectedColor)
{
ForEach(e => e.AssertColor(expectedColor));
}
public void AssertFontFamily(string expectedFontFamily)
{
ForEach(e => e.AssertFontFamily(expectedFontFamily));
}
public void AssertFontWeight(string expectedFontWeight)
{
ForEach(e => e.AssertFontWeight(expectedFontWeight));
}
public void AssertFontSize(string expectedFontSize)
{
ForEach(e => e.AssertFontSize(expectedFontSize));
}
public void AssertTextAlign(string expectedTextAlign)
{
ForEach(e => e.AssertTextAlign(expectedTextAlign));
}
public void AssertVerticalAlign(string expectedVerticalAlign)
{
ForEach(e => e.AssertVerticalAlign(expectedVerticalAlign));
}
private IEnumerable<IElement> GetAndWaitWebDriverElements()
{
var nativeElements = _elementFinder.FindAll(_by);
foreach (var nativeElement in nativeElements)
{
IElement element = new ElementAdapter(_driver, _by);
yield return element;
}
}
}
Now we can use the style assertions in the same way for a single element and an element collection!
