Extend The Framework to Fit Your Needs

To be sure that you will use BELLATRIX happily and frequently, we developed enormous ways to extend it. We have thus provided you with a wider test execution life cycle with the ability to plug-in everywhere.

Extended Test Execution Life Cycle and Plugins

Have a full control over your test execution.

All test frameworks give you the possibility to execute logic at certain points before or after tests. Typically tests are placed inside test classes. The standard test workflow for MSTest and NUnit is:

1. Execute Assembly Initialize – Once for the whole project
2. Execute Class Initialize – Once for the test class
3. Execute Test Initialize – Executed before each test
4. Execute Test
5. Execute Test Cleanup – Executed after each test
6. Execute Class Cleanup – Once for the test class
7. Execute Assembly Cleanup – Once for the whole project

Why do you need to extend the standard workflow?

Usually, you want to use these primary methods to execute business test-related logic. Sample use cases:

  • Take screenshots of test failure
  • Take videos on test failure
  • Fail tests if not executed under a certain time
  • Log test results in a 3rd party system
  • Control the app life cycle

In order to meet these requirements and allow you to write custom plugins, BELLATRIX comes with an extended test execution life cycle. When you write a plugin, you can execute logic in various phases of the new life cycle- before TestInitialize, after TestInitialize, before TestCleanup, after TestCleanup and more. For each test, all registered plugins logic is executed. This is how some of the BELLATRIX features work such as video and screenshot on test failure, browser life cycle, and more.

You can easily write your custom plugins. See how it is done. In BELLATRIX, we have a feature called “execution time under”. Here you specify for the test class the maximal allowed test execution time if some of the tests take longer to run- it fails automatically. This is done through a test workflow plugin.

This is how we specify the maximal allowed time.

 public class MeasuredResponseTimesTests : DesktopTest

This is part of the plugin.

public class ExecutionTimeUnderTestWorkflowPlugin : TestWorkflowPlugin
    protected override void PostTestInit(object sender, TestWorkflowPluginEventArgs e)
        // get the start time

    protected override void PostTestCleanup(object sender, TestWorkflowPluginEventArgs e)
        // total time = start time - current time
        // IF total time > specified time ===> FAIL TEST

In PostTestInit we start a stopwatch. We get the number placed inside the attribute in the PostTestCleanup method. Then stop the stopwatch and compare the actual and specified times. In case first took longer we fail the test.

Learn more

Element Action Hooks

Another way to extend BELLATRIX is to use the controls hooks.

This is how the BDD logging is implemented. For each control’s method, there are two hooks- one that is called “before the action” and one “after”. For example, the available hooks for the button are:

  • Clicking – an event executed before button click
  • Clicked – an event executed after the button is clicked
  • Hovering – an event executed before button hover
  • Hovered – an event executed after the button is hovered

You need to implement the event handlers for these events and subscribe to them.

public void ClickingEventHandler(object sender, ElementActionEventArgs arg)
    Logger.LogInformation($"Click {arg.Element.ElementName}");

public void HoveringEventHandler(object sender, ElementActionEventArgs arg)
    Logger.LogInformation($"Hover {arg.Element.ElementName}");

These are two sample event handlers for the BELLATRIX button where we log messages before clicking and hovering a button.

Learn more

Common Services Action Hooks

Another way to extend BELLATRIX is to use hooks for its common services.

The base class for all web elements- Element provides a few special events as well:

  • ScrollingToVisible – called before scrolling
  • ScrolledToVisible – called after scrolling
  • CreatingElement – called before creating the element
  • CreatedElement – called after the creation of the element
  • CreatingElements – called before the creation of nested element
  • CreatedElements – called after the creation of nested element
  • ReturningWrappedElement – called before searching for a native WebDriver element

Learn more

Extend Existing UI Elements

You have the ability to add new methods to existing BELLATRIX UI elements. You can add new methods in two ways.

Extension Methods

One way to extend an existing element is to create an extension method for the additional action.

public static class ButtonExtensions
    public static void SubmitButtonWithEnter(this Button button)
        var action = new Actions(button.WrappedDriver);

To use the new method, add a using statement to the extension methods’ namespace.


Click the “proceed to checkout” button via the Enter key instead of clicking on it.
Learn more

Child Elements

The second way of extending an existing element is to create a child element. Inherit the UI control you want to extend.

public class ExtendedButton : Button
    public void SubmitButtonWithEnter()
        var action = new Actions(WrappedDriver);

Next in your tests, use the ExtendedButton instead of the regular Button to have access to these methods.

ExtendedButton proceedToCheckout = 

Learn more

Extend Common Services

Ability to add a new method to BELLATRIX common services.

For example, a new way of login into your application. To extend the BELLATRIX common services you can create an extension method for the additional action.

public static class NavigationServiceExtensions
    public static void LoginToApp(this AppService appService, string userName, string password)
        var elementCreateService = new ElementCreateService();
        var userNameField = elementCreateService.CreateByAutomationId<TextField>("textBox");
        var passwordField = elementCreateService.CreateByAutomationId<Password>("passwordBox");
        var loginButton = elementCreateService.CreateByName<Button>("loginButton");


To use the new method, add a using statement to the extension methods’ namespace.

App.AppService.LoginToApp("bellatrix", "topSecret");

Learn more

Add New Find Locators

The ability to avoid code duplication and complex locate expressions by adding a custom find locators.

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

public class ByIdEndingWith : By
    private const string XpathEndingWithExpression = "//*[ends-with(@id, '{0}')]";

    public ByIdEndingWith(string value)
        : base(value)

    public override WindowsElement FindElement(WindowsDriver<WindowsElement> searchContext)
        => searchContext.FindElementByXPath(string.Format(XpathEndingWithExpression, Value));

    public override IEnumerable<WindowsElement> FindAllElements(WindowsDriver<WindowsElement> searchContext)
        => searchContext.FindElementsByXPath(string.Format(XpathEndingWithExpression, Value));

    public override AppiumWebElement FindElement(WindowsElement element)
        => element.FindElementByXPath(string.Format(XpathEndingWithExpression, Value));

    public override IEnumerable<AppiumWebElement> FindAllElements(WindowsElement element)
        => element.FindElementsByXPath(string.Format(XpathEndingWithExpression, Value));

    public override string ToString() => $"By ID ending with = {Value}";

We override all available methods and use XPath expression for finding an element with ID ending with. To ease the usage of the locator, we need to create an extension method of ElementCreateService.

public static TElement CreateByIdEndingWith<TElement>(this Element element, string idPart)
    where TElement : Element
    element.Create<TElement, ByIdEndingWith>(new ByIdEndingWith(idPart));

This is everything after that you can use your new locator as it was originally part of BELLATRIX.
Learn more

Add New Element Wait Methods

Ability to add your custom element wait logic.

Imagine that you want to wait for an element to have a specific content. First, create a new class and inherit the BaseUntil class.

public class UntilHasContent : BaseUntil
    private readonly string _elementStyle;

    public UntilHasContent(string elementStyle, int? timeoutInterval = null, int? sleepInterval = null)
        : base(timeoutInterval, sleepInterval)
        _elementStyle = elementStyle;

   public override void WaitUntil<TBy>(TBy by) => WaitUntil(ElementHasContent(WrappedWebDriver, by), TimeoutInterval, SleepInterval);

    private Func<IWebDriver, bool> ElementHasContent<TBy>(WindowsDriver<WindowsElement> searchContext, TBy by)
        where TBy : Locators.By => driver =>
            var element = by.FindElement(searchContext);
            return !string.IsNullOrEmpty(element.Text);
        catch (NoSuchElementException)
            return false;
        catch (InvalidOperationException)
            return false;

The important part is located in the ElementHasContent function. There we find the element and check the current value in the Text attribute. The internal WaitUntil will wait until the value changes in the specified time. The next and final step is to create an extension method for all UI elements.

public static TElementType ToHasContent<TElementType>(this TElementType element, 
int? timeoutInterval = null, int? sleepInterval = null)
 where TElementType : Element
    var until = new UntilHaveContent(timeoutInterval, sleepInterval);
    return element;

After UntilHasContent is created, it is important to be passed on to the element’s EnsureState method. From now on, the method is available as it was originally part of the framework.

Learn more

    Autogerated Test Cases

    You might be interested also in our online trainings

    Learn How to write automated tests for web, desktop, mobile, or API apps using WebDriver, Appium, SpecFlow, and other industry-leading tools/frameworks.

    See Our Trainings