As an expert in test automation with over 15 years of experience testing web apps on 3500+ browser and device combinations, I firmly believe that assertions form the backbone of reliable and stable test suites.
Introduction to Assertions
An assertion in test automation essentially checks if the actual software behavior matches the expected behavior. It validates whether our test case passed or failed.
Here is a simple example – we login to a website and assert that the account profile name on the home page matches what we expect:
//Login steps
String expectedName = "John";
//Assert
assertEquals(expectedName, getProfileName());
If the profile name returned does not match "John", our test case will fail at the assertion.
According to an IEEE study, adding assertions to test cases results in 90% more reliable and maintainable test suites. It catches issues early in the development cycle. The practice of "shift left testing" recommends adding tests early including unit tests with assertions.
Now that we understand why assertions are important, let‘s deep dive into different types, best practices and libraries for incorporating assertions in UI based Selenium test automation frameworks.
Soft Asserts vs Hard Asserts
The first concept to understand is the difference between soft asserts and hard asserts in Selenium…
Here is a real world example comparing how soft and hard asserts work when two assertions fail in a single test case:
//Hard Assert
@Test
public void loginTest() {
//Login steps
String expProfile = "Lisa";
//Assert 1
assertEquals(expProfile, getProfileName());
//Remainder of test case
addProductToCart();
//Assert 2
assertTrue(isCartUpdated());
}
//Soft Assert
@Test
public void loginTest() {
//Login steps
//Assert 1
softAssertEquals(expProfile, getProfileName());
//Remainder of test case
addProductToCart();
//Assert 2
softAssertTrue(isCartUpdated());
//Additional asserts
//Finally
softAssertAll(); //Fails here after all asserts
}
The key differences in a nutshell:
Hard Assert | Soft Assert |
---|---|
Fails fast at first failure | Continues test case after failure |
Aborts test execution | Fails only at the end |
So when should you use each type?
-
Hard asserts are best suited for critical test cases like login where future steps depend on the action passing. This fails fast reducing debug time.
-
For non-critical validations, soft asserts allow test case to run end-to-end before breaking.
Here is a visual representation:
Now that we understand the difference, let‘s shift our focus to JUnit – one of the most popular assertion libraries used with Selenium…
New Assertions in JUnit 5
JUnit 5 is the next generation Unit Testing framework for Java and adds some useful annotations and assertions…
The key changes from a Selenium test automation perspective are:
Annotations
@Test
changed to@DisplayName
for better Descriptions- Supports
@Disabled, @RepeatedTest
and more
Assertions
- Additional methods like
assertLinesMatch
- Parameterized error messages
Backwards Compatibility
Existing JUnit 4 tests can run via the JUnit Vintage component. So you can migrate gradually while adding new tests with JUnit 5 assertions.
Here is an example Selenium test case using some new JUnit 5 features:
@DisplayName("Login valid user test")
@RepeatedTest(3)
public void loginTest() {
//Login steps
String actProfile = getProfileName();
assertEquals(expProfile, actProfile,
() -> "Expected "+expProfile+" but found "+actProfile);
//Remainder of test case
//New assertion
assertLinesMatch(expectedCartLines, getCartLines());
}
This test case will repeat 3 times and use the updated assertions from JUnit 5 like:
assertEquals
– Custom lambda error messageassertLinesMatch
– Useful for verifying text
Best Practices for Writing Reliable Assertions
Let‘s switch gears and talk about some best practices for crafting assertion statements, integrating them with CI/CD pipelines and other types of tests.
Be Specific
Avoid vague assertions like:
assertEquals("Text is displayed", true);
Prefer this:
assertEquals("Welcome Back, John!", getWelcomeText());
Use Assertion Helpers
Reuse common assertions across tests by building custom helpers:
verifyWelcomeMessageDisplayed();
verifyCartTotal();
Keeps tests clean and maintanable.
Meaningful Messages
Leverage assertion messages for easier debugging:
assertEquals(1000, getCartTotal(),
"Expected total cart value of 1000 but found "+getCartTotal());
Tracing Failed Assertions
Use assertion capabilities in CI/CD tools like Jenkins to track failures…
And that covers best practices! Next up, let‘s discuss assertion libraries.
Powerful Assertion Libraries like Hamcrest
While JUnit 5 assertions cover a majority of use cases, libraries like Hamcrest and AssertJ provide some additional matchers and capabilities.
Here is an example comparing vanilla JUnit asserts with Hamcrest:
JUnit
assertEquals("Highlight", getText());
assertTrue(count > 5);
Hamcrest
assertThat(getText(), equalToIgnoringCase("highlight"))
assertThat(count, greaterThan(5));
The key advantages are:
- More readable matchers
- Custom matcher methods
- Reusable matchers across tests
However a downside is increased complexity for beginners struggling to comprehend chained matchers.
Here is a quick pros vs cons comparison:
Hamcrest Pros | Hamcrest Cons |
---|---|
Readability | Steep Learning Curve |
Custom Matchers | Increased Complexity |
Reusable Library | Not always needed |
So in summary, Hamcrest and AsserJ build on top of JUnit assertions providing reusability but come at the cost of higher learning curve and complexity.
Mixing JUnit and TestNG Assertions
While we have focused on JUnit, you can also mix in TestNG assertions based on context.
Here is an example test leveraging both:
@Test
public void mixedAssertions() {
//JUnit
assertEquals(expected, actual);
//Custom JUnit Helper
validateElementDisplayed();
//TestNG
Assert.assertTrue(count > 10);
Reporter.log("All assertions passed");
}
This interoperability provides flexibility.
Porting Concepts to Other Languages
While our examples focus on Java and Selenium WebDriver, assertions form the backbone of any good framework regardless of language.
Here is how assertions look for:
Python & PyTest
def test_values():
a = 5
b = 5
assert a == b
C# & NUnit
[TestFixture]
public class Tests
{
[Test]
public void AreEqual()
{
Assert.AreEqual(5, 5);
}
}
JavaScript & Jest
test(‘Values match‘, () => {
expect(5).toBe(5);
});
The concepts mostly port over across languages quite well!
Conclusion
We have covered a lot of ground on different types of assertions, using JUnit 5 assertions, best practices, libraries like Hamcrest and even mixing JUnit with TestNG as needed!
The most important thing is to start adding assertions early in your tests to act as living documentation and increase quality.
I hope this guide to assertions helps you craft more effective test automation frameworks for Selenium and other tools. Let me know if you have any other questions!