Serenity Ensure – Fluent Assertions in Serenity Screenplay

John Ferguson Smart | Mentor | Author | Speaker - Author of 'BDD in Action'.
Helping teams deliver more valuable software sooner23rd June 2019

Assertions are an important part of any test automation framework, and Serenity gives us many options. You can of course use standard JUnit, Hamcrest or AssertJ assertions at any point in a Screenplay test. But more recent versions of Serenity Screenplay provide an alternative approach, which many developers find easier to use and faster to write: the serenity-ensure module.

Introducing serenity-ensure

The Ensure class produces a Performable, so you can integrate them directly into the attemptsTo() method. It also has a very readable DSL and lets you use code completion to discover the assertions you can use for different values, making writing assertions easier and quicker.

An example of code equivalent to the above can be seen here. Suppose you have an input field on your HTML form to enter the age, which you have identified in your Screenplay test using a Target:

Target AGE_FIELD = Target.the("Age field").locatedBy("#age");

Using serenity-ensure, you could check the value of this field using the Ensure.that() method, like this:

...
sam.attemptsTo(
    Ensure.that(AGE).value().isEqualTo("40") 
);

Getting started with serenity-ensure assertions

To add serenity-ensure to your project,  you need to add a new dependency, as shown here for Maven:

<dependency>
 <groupId>net.serenity-bdd</groupId>
 <artifactId>serenity-ensure</artifactId>
 <version>${serenity.version}</version>
 <scope>test</scope>
 </dependency>

The starting point for all serenity-ensure assertions is the  net.serenitybdd.screenplay.ensure.Ensure class.

From here, you use the Ensure.that() method, passing the value you want to check. This can be an actual value (String, Integer, LocalDate etc), a Screenplay Question, or a web element locator. The actual assertions available can be different for different data types, and this is one of the things that makes serenity-ensure so convenient to use: Once you have entered  Ensure.that(someValue) in your IDE, you can use auto-complete to explore the range of available assertions for that type. There are over a hundred different assertion methods, covering web elements, Strings, Dates and Times, integer and floating point numbers, and collections. You can see the full set of assertions in the Serenity Documentation, but let's take a look at some of the highlights.

Working with assertions about web elements

The serenity-ensure module has a rich range of assertions related to web elements. We have already seen some of these in the previous code. Other assertions include isDisplayed() and isEnabled(). We can use either a Target instance directly, or use the ElementLocated.by() method to specify a Target, a By locator, or a CSS or XPath expression. For example:

aster.attemptsTo(
    Ensure.that(ElementLocated.by("#firstName")).isDisplayed()
);

You can use methods like value(), text() or textContent() to read from a web element. Once you have obtained the value, you can use a range of String assertions, such as startsWith() shown here:

aster.attemptsTo(
    Ensure.that(FIRST_NAME).value().startsWith("Joe")
);

Working with assertions about collections

We often need to reason about collections of values, rather than just individual values. We can use methods like values() and textContentValues() to make assertions about collections of web elements. For example:

aster.attemptsTo(
    Ensure.that(ElementLocated.by("#colors option"))
          .values()
          .contains("red","blue","green") );

We can also make assertions about the elements themselves. TheMatchingElement class gives us a set of methods to make assertions about these elements, and methods such as allMatch(), anyMatch() and noneMatch() complete the picture:

aster.attemptsTo(
    Ensure.thatTheSetOf(ElementsLocated.by(".train-line"))
          .allMatch(TheMatchingElement.containsText("Line"))
);

Converting types

We can also use converter methods such as asAnInteger(), asADouble, or asADate() to convert the String value returned by WebDriver into a form more convenient to make assertions on. For example, we could refactor the code we saw earlier to reason about the age value as an integer like this:

...
sam.attemptsTo(
    Ensure.that(AGE).value().asAnInteger().isGreaterThan(18) 
);

Alternatively, if you wanted to make an assertion about a date or a time, you could write code like the following:

aster.attemptsTo(
    Ensure.that(ElementLocated.by("#currentDate"))
          .value()
          .asADate("dd-MM-yyyy")
          .isBefore(dateLimit)
);

Want to learn more?

This is just a very brief taster of what the serenity-ensure module can do. You can read the full documentation for the serenity-ensure module here. If you want to learn more about Serenity Screenplay, you can follow the introductory tutorial here. Or if you prefer video tutorials and guided exercises, the Serenity Dojo Screenplay Course will be getting a module on serenity-ensure later this week.

© 2019 John Ferguson Smart