Test Diary

Archives


Social Profile


Recent Comments


Alternatives to Asserts (AssertJ)

Olufemi Ade-OlusileOlufemi Ade-Olusile

The Problem

The standard TestNG / JUnit asserts are widely used in testing, however they are not flexible enough for functional tests in several cases.

Possible Solutions

This post and possible solution was inspired by a friend of mine (Robert Ambrus). Anyway, we looked at some common java open source libraries which have an active community and provide clean code and extendability.

Candidates:

JUnit + Hamcrest – Library of matchers for building test expressions

Hamcrest is a framework for writing matcher objects allowing ‘match’ rules to be defined declaratively. There are a number of situations where matchers are invaluble, such as UI validation, or data filtering, but it is in the area of writing flexible tests that matchers are most commonly used.

Google Truth – We’ve made failure a strategy

Truth is a testing framework designed to make your tests and their error messages more readable and discoverable, while being extensible to new types of objects.

Truth adopts a fluent style for test propositions, is extensible in several ways, supports IDE completion/discovery of available propositions, and supports different responses to un-true propositions. Truth can be used to declare JUnit-style assumptions (which skip the test on failure), assertions (interrupt the test on failure), and expectations (continue the test, but collect errors and report failure at the end).

AssertJ – Fluent Assertions for Java

AssertJ core is a Java library that provides a fluent interface for writing assertions. Its main goal is to improve test code readability and make maintenance of tests easier.
AssertJ core provides assertions for JDK standard types can be used with either JUnit or TestNG.

Examples

We collected a bunch of assertions that are frequently used in functional tests and implemented using various technologies.

Pure TestNG Asserts

//Numeric Asserts
Assert.assertEquals(3, 3, "Numeric Assert - equals");
Assert.assertTrue(4 > 3, "Numeric Assert - greater than");
Assert.assertTrue(2 < 3, "Numeric Assert - less than");
Assert.assertTrue(4.1 - 3.9 < 0.5, "Numeric Assert - almost equals (equals with tolerance)");

//Boolean Asserts
Assert.assertTrue(true, "Boolean Assert - true");
Assert.assertFalse(false, "Boolean Assert - false");

//String Asserts
Assert.assertEquals("London", "London", "String Assert - equals");
Assert.assertTrue("London".equalsIgnoreCase("london"), "String Assert - equals ignore case");
Assert.assertTrue("London".contains("on"), "String Assert - contains");
Assert.assertFalse("London".contains("in"), "String Assert - does not contain");
Assert.assertTrue("London".startsWith("Lon"), "String Assert - starts with");
Assert.assertTrue("London".endsWith("don"), "String Assert - ends with");
Assert.assertTrue("London".matches("L.*n"), "String Assert - matches");
Assert.assertTrue(StringUtils.isNotBlank("London") && !"London".matches("^[\\w\\d_]+(\\.[\\w\\d_]+)+$"), "String Assert - not i18n key");

//List Asserts
Assert.assertEquals(Arrays.asList("Smoking", "Non-smoking"), Arrays.asList("Smoking", "Non-smoking"), "List Assert - equals");
Assert.assertTrue(Arrays.asList("Smoking", "Non-smoking").contains("Smoking"), "List Assert - contains element");
Assert.assertTrue(Arrays.asList("Smoking", "Non-smoking").containsAll(Arrays.asList("Smoking", "Non-smoking")), "List Assert - contains list");

//Object Asserts
Assert.assertEquals(LocalDate.now(), LocalDate.now(), "Object Assert - equals");

It does the Job, however the readability is poor. In most cases we end up using assertTrue / assertFalse with a logical condition.

JUnit Assert with Hamcrest

// Numeric Asserts
Assert.assertThat("Numeric Assert - equals", 3, is(equalTo(3)));
Assert.assertThat("Numeric Assert - greater than", 4, is(greaterThan(3)));
Assert.assertThat("Numeric Assert - less than", 2, is(lessThan(3)));
Assert.assertThat("Numeric Assert - almost equals (equals with tolerance)", 4.1, is(closeTo(3.9, 0.5)));

// Boolean Asserts
Assert.assertThat("Boolean Assert - true", true, is(true));
Assert.assertThat("Boolean Assert - false", false, is(false));

// String Asserts
Assert.assertThat("String Assert - equals", "London", is(equalTo("London")));
Assert.assertThat("String Assert - equals ignore case", "London", is(equalToIgnoringCase("london")));
Assert.assertThat("String Assert - contains", "London", containsString("on"));
Assert.assertThat("String Assert - does not contain", "London", not(containsString("in")));
Assert.assertThat("String Assert - starts with", "London", startsWith("Lon"));
Assert.assertThat("String Assert - ends with", "London", endsWith("don"));
Assert.assertThat("String Assert - matches", "London", matches("L.*n")); //not supported out of the box!!!
Assert.assertThat("String Assert - not i18n key", "London", not(i18nKey())); //not supported out of the box!!!

// List Asserts
Assert.assertThat("List Assert - equals", Arrays.asList("Smoking", "Non-smoking"), is(equalTo(Arrays.asList("Smoking", "Non-smoking"))));
Assert.assertThat("List Assert - contains element", "Smoking", anyOf(equalTo("Smoking"), equalTo("Non-smoking")));
Assert.assertThat("List Assert - contains list", Arrays.asList("Smoking", "Non-smoking"), contains("Smoking", "Non-smoking"));

// Object Asserts
Assert.assertThat("Object Assert - equals", LocalDate.now(), is(equalTo(LocalDate.now())));

JUnit’s assertThat brings some flexibility, Hamcrest matcher is pretty cool, makes the code more clean, however the huge number of static imports could be annoying.

As you can see, string regexp matcher is not supported out of the box, but can be implemented easily:


private Matcher<String> matches(final String pattern) {
return new BaseMatcher<String>() {
@Override
public boolean matches(final Object item) {
return ((String) item).matches(pattern);
}
@Override
public void describeTo(final Description description) {
description.appendText(" should match with ").appendValue(pattern);
}
};
}

Google Truth

// Numeric Asserts
Truth.assertWithMessage("Numeric Assert - equals").that(3).isEqualTo(3);
Truth.assertWithMessage("Numeric Assert - greater than").that(4).isGreaterThan(3);
Truth.assertWithMessage("Numeric Assert - less than").that(2).isLessThan(3);
Truth.assertWithMessage("Numeric Assert - almost equals (equals with tolerance)").that(4.1).isWithin(0.5).of(3.9);

// Boolean Asserts
Truth.assertWithMessage("Boolean Assert - true").that(true).isTrue();
Truth.assertWithMessage("Boolean Assert - false").that(false).isFalse();

// String Asserts
Truth.assertWithMessage("String Assert - equals").that("London").isEqualTo("London");
Truth.assertWithMessage("String Assert - equals ignore case").that("London").matches(Pattern.compile("london", Pattern.CASE_INSENSITIVE));
Truth.assertWithMessage("String Assert - contains").that("London").contains("on");
Truth.assertWithMessage("String Assert - does not contain").that("London").doesNotContain("in");
Truth.assertWithMessage("String Assert - starts with").that("London").startsWith("Lon");
Truth.assertWithMessage("String Assert - ends with").that("London").endsWith("don");
Truth.assertWithMessage("String Assert - matches").that("London").matches("L.*n");
Truth.assertWithMessage("String Assert - not i18n key").that("London").doesNotMatch(I18N_PATTERN);

// List Asserts
Truth.assertWithMessage("List Assert - equals").that(Arrays.asList("Smoking", "Non-smoking")).containsExactly("Smoking", "Non-smoking");
Truth.assertWithMessage("List Assert - contains element").that("Smoking").isAnyOf("Smoking","Non-smoking");
Truth.assertWithMessage("List Assert - contains list").that(Arrays.asList("Smoking", "Non-smoking")).containsAllOf("Smoking", "Non-smoking");

// Object Asserts
Truth.assertWithMessage("Object Assert - equals").that(LocalDate.now()).isEqualTo(LocalDate.now());

Looks pretty nice, provides a lot of matchers out of the box, checked the release history and it seems to be a start up project, which kicked off a couple of years ago and has a few number of users, with not many resources available.

AssertJ

// Numeric Asserts
Assertions.assertThat(3).as("Numeric Assert - equals").isEqualTo(3);
Assertions.assertThat(4).as("Numeric Assert - greater than").isGreaterThan(3);
Assertions.assertThat(2).as("Numeric Assert - less than").isLessThan(3);
Assertions.assertThat(4.1).as("Numeric Assert - almost equals (equals with tolerance)").isCloseTo(3.9, within(0.5));

// Boolean Asserts
Assertions.assertThat(true).as("Boolean Assert - true").isTrue();
Assertions.assertThat(false).as("Boolean Assert - false").isFalse();

// String Asserts
Assertions.assertThat("London").as("String Assert - equals").isEqualTo("London");
Assertions.assertThat("London").as("String Assert - equals ignore case").isEqualToIgnoringCase("london");
Assertions.assertThat("London").as("String Assert - contains").contains("on");
Assertions.assertThat("London").as("String Assert - does not contain").doesNotContain("in");
Assertions.assertThat("London").as("String Assert - starts with").startsWith("Lon");
Assertions.assertThat("London").as("String Assert - ends with").endsWith("don");
Assertions.assertThat("London").as("String Assert - matches").matches("L.*n");
Assertions.assertThat("London").as("String Assert - not i18n key").isNotEmpty().doesNotMatch(I18N_PATTERN);

// List Asserts
Assertions.assertThat(Arrays.asList("Smoking", "Non-smoking")).as("List Assert - equals").containsExactly("Smoking", "Non-smoking");
Assertions.assertThat("Smoking").as("List Assert - contains element").isIn("Smoking","Non-smoking");
Assertions.assertThat(Arrays.asList("Smoking", "Non-smoking")).as("List Assert - contains list").contains("Smoking", "Non-smoking");

// Object Asserts
Assertions.assertThat(LocalDate.now()).as("Object Assert - equals").isEqualTo(LocalDate.now());

Looks pretty nice, provides a lot of matchers out of the box, seems to be a popular library, lots of resources available.

Outro

The alternatives look pretty cool, all of them have some advantages / disadvantages. Anyway, in overall I prefer AssertJ, since:

“My name is Olufemi Ade-Olusile, and I am a Software Developer in Test. I am the owner of "Test diary" which is aimed at inspiring and teaching software testers.