Full-Stack Test Automation Frameworks- API Usability Part 2

Full-Stack Test Automation Frameworks- API Usability Part 2

In the last article from the series, we talked about API usability, one of the must-have features of the full-stack test automation frameworks. Here I am going to show you how API usability comes hand in hand with extensibility features. You will see examples how to create additional element locators and waits. Additionally, we are going to talk about typified elements for accelerating tests development and making tests more readable.

Note

You cannot just copy-paste or use most of the examples directly, except you use BELLATRIX framework. However, you can get lots of ideas on how you can make your test automation framework easier to use, extend and customize.

Add New Find Locators

In the last article, I showed you how the element locators could be improved in the 5th generation test automation frameworks. As mentioned, API usability should come with the flexibility of extending the framework. Part of this is to be able to add new locators which is almost an impossible job in vanilla WebDriver.

Note

Vanilla stands for examples that use only standard WebDriver code without anything else.

To skip code duplication and complex locators, you may need to add custom find locator. It is relatively easy to do it with BELLATRIX

Here is a sample implementation of locator for finding all elements starting with ID. First we need to create the By locator.

public class ByIdEndingWith : By
{
    public ByIdEndingWith(string value)
    : base(value)
    {
    }
    public override OpenQA.Selenium.By Convert()
    {
        return OpenQA.Selenium.By.CssSelector($"[id^='{Value}']");
    }
}

In the Convert method, we use standard WebDriver By locator in this case we implement our requirement through little CSS.

To ease the usage of the locator, we need to create an extension method of ElementCreateService.

public static TElement CreateByIdStartingWith<TElement>(this ElementCreateService repository, string idStarting)
where TElement : Element
{
    repository.Create<TElement, ByIdStartingWith>(new ByIdStartingWith(idStarting));
}

This is everything after that you can use your new locator as it was originally part of BELLATRIX. Find how to add custom locators for web, desktop, Android and iOS.

Add New Element Wait Methods

In the last article, we talked how difficult can be to wait for elements with vanilla WebDriver. I showed you how we elegantly could solve this problem with ToBe extension wait methods. Here, I will continue with the subject by showing you how to add custom wait methods.

Imagine that you want to wait for an element to have specific style.

First, create a new class and inherit BaseUntil class.

public class UntilHasStyle : BaseUntil
{
    private readonly string _elementStyle;
    public UntilHasStyle(string elementStyle, int? timeoutInterval = null, int? sleepInterval = null)
    : base(timeoutInterval, sleepInterval)
    {
        _elementStyle = elementStyle;
    }
    public override void WaitUntil<TBy>(TBy by)
    {
        WaitUntil(ElementHasStyle(WrappedWebDriver, by), TimeoutInterval, SleepInterval);
    }
    private Func<IWebDriver, bool> ElementHasStyle<TBy>(ISearchContext searchContext, TBy by)
    where TBy : By
    {
        return driver =>
        {
            try
            {
                var element = FindElement(searchContext, by);
                return element != null && element.GetAttribute("style").Equals(_elementStyle);
            }
            catch (StaleElementReferenceException)
            {
                return false;
            }
        };
    }
}

The important part is located in the ElementHasStyle function. There we find the element and check the current value in the style attribute. The internal WaitUntil will wait until the value changes in the specified time.

The next and final step is create an extension method for all UI elements.

public static TElementType ToHasStyle<TElementType>(
this TElementType element,
string style,
int? timeoutInterval = null,
int? sleepInterval = null)
where TElementType : Element
{
    var until = new UntilHasStyle(style, timeoutInterval, sleepInterval);
    element.EnsureState(until);
    return element;
}

After UntilHasStyle is created is important to be passed to element’s EnsureState method.

From now on the method is available as it was originally part of the framework.

You can add similar methods with BELLATRIX for web, desktop, Android and iOS.

Typified Elements

Vanilla WebDriver Example

IWebElement agreeCheckBox = driver.FindElement(By.Id("agreeChB"));
agreeCheckBox.Click();
IWebElement firstNameTextField = driver.FindElement(By.Id("firstName"));
firstNameTextField.SendKeys("John");
IWebElement avatarUpload = driver.FindElement(By.Id("uploadAvatar"));
avatarUpload.SendKeys("pathTomyAvatar.jpg");
IWebElement saveBtn = driver.FindElement(By.Id("saveBtn"));
agreeCheckBox.SendKeys(Keys.Enter);

BELLATRIX Example

CheckBox agreeCheckBox = App.ElementCreateService.CreateById<CheckBox>("agreeChB");
agreeCheckBox.Check();
TextField firstNameTextField = App.ElementCreateService.CreateById<TextField>("firstName");
firstNameTextField.SetText("John");
InputFile avatarUpload = App.ElementCreateService.CreateById<InputFile>("uploadAvatar");
avatarUpload.Upload("pathTomyAvatar.jpg");
Button saveBtn = App.ElementCreateService.CreateById<Button>("saveBtn");
saveBtn.ClickByEnter();

While reading the code, you can quickly find what is the type of the elements since it is the first thing you see. Moreover, the specific action methods and properties make the code more self-explanatory.

Instead of using Selenium.Support package for selecting elements we created separate web control for the job.

BELLATRIX Example

Select billingCountry = App.ElementCreateService.CreateById<Select>("billing_country");
billingCountry.SelectByText("Bulgaria");

Here is the same example written with vanilla WebDriver. First, you need to install additional package- Selenium.Support.

Vanilla WebDriver Example

IWebDriver driver;
IWebElement billingCountry = driver.FindElement(By.Id("billing_country"));
var billingCountrySelectElement = new SelectElement(billingCountry);
billingCountrySelectElement.SelectByText("Bulgaria");

Or we bring new useful methods to default controls such as Focus and Hover.

BELLATRIX Example

var confirmBtn = App.ElementCreateService.CreateById<Button>("confirm");
confirmBtn.Hover();
confirmBtn.Focus();

To hover an element directly with WebDriver you can use the following code.

Vanilla WebDriver Example

IWebDriver driver;
IJavaScriptExecutor jsDriver = (IJavaScriptExecutor)driver;
jsDriver.ExecuteScript("arguments[0].onmouseover();", element);

It is not very user-friendly, don’t you think? Nor readable.

See a similar example how to focus an element with vanilla WebDriver.

Vanilla WebDriver Example

IWebDriver driver;
IJavaScriptExecutor jsDriver = (IJavaScriptExecutor)driver;
jsDriver.ExecuteScript("arguments[0].focus();", element);

Most important attributes of each web control are included and their assertion alternatives.

BELLATRIX Example

var confirmBtn = App.ElementCreateService.CreateById<Button>("confirm");
confirmBtn.EnsureAccessKeyIs(5);
var productQuantity = App.ElementCreateService.CreateById<Number>("qt_number");
productQuantity.EnsureMaxIs(5);

Automating HTML 5 Web Controls

Test automation framework API usability can be further enhanced through introduction of HTML 5 web controls. These controls c****annot be automated out of the box with vanilla WebDriver.

BELLATRIX Example

Phone billingPhone = App.ElementCreateService.CreateById<Phone>("billing_phone");
billingPhone.SetPhone("+00359894646464");
Color carColor = App.ElementCreateService.CreateById<Color>("car_color");
carColor.SetColor("#f00030");
Time timeElement = App.ElementCreateService.CreateById<Time>("time");
timeElement.SetTime(12, 12);

To set time in HTML 5 control using WebDriver you can write something like below.

Vanilla WebDriver Example

void SetTime(int hours, int minutes)
{
    IWebDriver driver;
    IJavaScriptExecutor jsDriver = (IJavaScriptExecutor)driver;
    jsDriver.ExecuteScript("arguments[0].setAttribute(value, arguments[1]);", element, $"{hours}:{minutes}:00");
}

Summary

I showed you how API Usability comes hand in hand with API extensibility. You learned how you can add new find locators and element wait methods. Also, we talked about typified elements and how they can significantly improve API user-friendliness and readability of your tests, hiding low-level unreadable vanilla WebDriver code. In next article, I will show you how you can improve the readability of your tests though BDD logging using more extensibility features.

Related Articles

Design Architecture, Design Patterns

Failed Tests Аnalysis – Decorator Design Pattern

Here I will present to you the third version of the Failed Tests Analysis engine part of the Design Patterns in Automated Testing Series. We are going to utilis

Failed Tests Аnalysis – Decorator Design Pattern

Design Architecture

Unit Testing Guidelines What to Test And What Not

During the years of consulting, many people asked me to help them get started to write unit tests. During the process always pop up one question- "What should I

Unit Testing Guidelines What to Test And What Not

Design Architecture

ATOM Model - Advanced Testing Optimization Maturity Model

This article introduces the Advanced Testing Optimization Maturity (ATOM) Model. It's an innovative approach for assessing and boosting test automation in organ

ATOM Model - Advanced Testing Optimization Maturity Model

Design Architecture

5 Must-Have Features of Full-Stack Test Automation Frameworks Part 1

Nowadays, engineers shouldn't be limited which OS they use. By definition, frameworks should be completely generic, and they shouldn't restrict their users. Whi

5 Must-Have Features of Full-Stack Test Automation Frameworks Part 1

Design Architecture

Full-Stack Test Automation Frameworks- API Usability Part 1

In one of the last articles from the series, we talked about tons of problems that modern test automation frameworks should be able to solve. The full-stack tes

Full-Stack Test Automation Frameworks- API Usability Part 1

Design Architecture

Assessment System for Tests’ Architecture Design- SpecFlow Based Tests

In my previous article Assessment System for Tests’ Architecture Design, I presented to you eight criteria for system tests architecture design assessment. To u

Assessment System for Tests’ Architecture Design- SpecFlow Based Tests
Anton Angelov

About the author

Anton Angelov is Managing Director, Co-Founder, and Chief Test Automation Architect at Automate The Planet — a boutique consulting firm specializing in AI-augmented test automation strategy, implementation, and enablement. He is the creator of BELLATRIX, a cross-platform framework for web, mobile, desktop, and API testing, and the author of 8 bestselling books on test automation. A speaker at 60+ international conferences and researcher in AI-driven testing and LLM-based automation, he has been recognized as QA of the Decade and Webit Changemaker 2025.