Latest updates in your mailbox

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.

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.

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 webdesktopAndroid 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 webdesktopAndroid and iOS.

Typified Elements

Since WebDriver is not a test framework, it is written in most general manner. All web elements share common IWebElement interface- containing the same methods and properties.

For example- you use the SendKeys method to type text in a text field, clicking a button using Enter key or uploading a file. Or why do you need Click method for DIV or text field?

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);

One more thing, if you don’t use suffixes in the names of your variables or properties, you won’t know what type of elements they are. In case you want to perform specific verifications, this might be important. For example- anchor web elements cannot be disabled but buttons can.

BELLATRIX offers over 30 strongly typed web controls such as- anchor, button, text field, checkbox, etc. Each specific control contains well-named actions and properties that are related to it.

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].onmouseover();", 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 cannot 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 the next article, I will show you how you can improve the readability of your tests though BDD logging using more extensibility features.

Try Now Bellatrix

Build up to 100 tests using full product capabilities.

Download