AssertJ has many great features that not everybody is aware of, here are some of them.
Basic tips :
Iterable and arrays assertions :
Advanced tips :
We want to start typing asser and let code completion suggest assertThat from AssertJ (and not the one from Hamcrest !).
Eclipse configuration
IntelliJ Idea
It is often valuable to describe the assertion performed, especially for boolean assertion where the default error message just complains that it got false instead of true (or vice versa).
You can set such a description with as(String description, Object... args) but remember to do it before calling the assertion. Otherwise it is simply ignored as a failing assertion breaks the following chained calls.
Example of a failing assertion with a description :
TolkienCharacter frodo = new TolkienCharacter("Frodo", 33, HOBBIT);
// failing assertion, remember to call as() before the assertion, not after !
assertThat(frodo.getAge()).as("check %s's age", frodo.getName()).isEqualTo(100);
The error message starts with the given description in [] :
[check Frodo's age] expected:<100> but was:<33>
Filtering can be done on arrays or iterables. Filter criteria are expressed by :
Let's see both options in some examples taken from FilterExamples.java of the assertj-examples project.
Filtering with a Predicate
You specify the filter condition using simple predicate, best expressed with a lambda.
Example :
assertThat(fellowshipOfTheRing).filteredOn( character -> character.getName().contains("o") )
.containsOnly(aragorn, frodo, legolas, boromir);
Filtering on a property or a field
First you specify the property/field name to filter on and then its expected value. The filter first tries to get the value from a property, then from a field. Reading private fields is supported by default, but can be disabled globally by calling Assertions.setAllowExtractingPrivateFields(false).
Filters support reading nested properties/fields. Note that if an intermediate value is null the whole nested property/field is considered to be null. Thus reading "address.street.name" will return null if "address.street" is null.
Filters support these basic operations : not, in, notIn
import static org.assertj.core.api.Assertions.in;
import static org.assertj.core.api.Assertions.not;
import static org.assertj.core.api.Assertions.notIn;
...
// filters use introspection to get property/field values
assertThat(fellowshipOfTheRing).filteredOn("race", HOBBIT)
.containsOnly(sam, frodo, pippin, merry);
// nested properties are supported
assertThat(fellowshipOfTheRing).filteredOn("race.name", "Man")
.containsOnly(aragorn, boromir);
// you can apply different comparison
assertThat(fellowshipOfTheRing).filteredOn("race", notIn(HOBBIT, MAN))
.containsOnly(gandalf, gimli, legolas);
assertThat(fellowshipOfTheRing).filteredOn("race", in(MAIA, MAN))
.containsOnly(gandalf, boromir, aragorn);
assertThat(fellowshipOfTheRing).filteredOn("race", not(HOBBIT))
.containsOnly(gandalf, boromir, aragorn, gimli, legolas);
// you can chain multiple filter criteria
assertThat(fellowshipOfTheRing).filteredOn("race", MAN)
.filteredOn("name", not("Boromir"))
.containsOnly(aragorn);
Filtering with a Condition
Filter the iterable/array under test keeping only elements matching the given Condition.
Two methods are available : being(Condition) and having(Condition). They do the same job - pick the one that makes your code more readable !
import org.assertj.core.api.Condition;
Condition<Player> mvpStats= new Condition<Player>() {
@Override
public boolean matches(Player player) {
return player.pointsPerGame() > 20 && (player.assistsPerGame() >= 8 || player.reboundsPerGame() >= 8);
}
};
List<Player> players;
players.add(rose); // Derrick Rose : 25 ppg - 8 assists - 5 rebounds
players.add(lebron); // Lebron James : 27 ppg - 6 assists - 9 rebounds
players.add(noah); // Joachim Noah : 8 ppg - 5 assists - 11 rebounds
// noah does not have more than 20 ppg
assertThat(players).filteredOn(mvpStats)
.containsOnly(rose, lebron);
Let's say you have queried some service/dao and got a list (or an array) of TolkienCharacters. To check the results you have to build the expected TolkienCharacter(s), which can be a lot of work !
List<TolkienCharacter> fellowshipOfTheRing = tolkienDao.findHeroes(); // frodo, sam, aragorn ...
// requires creation of frodo and aragorn, the expected TolkienCharacters
assertThat(fellowshipOfTheRing).contains(frodo, aragorn);
Instead, it is usually sufficient to only check some fields or properties of the TolkienCharacter results elements. This easy but you still have to write code to extract the fields/properties before performing your assertions, something like :
// extract the names ...
List<String> names = new ArrayList<String>();
for (TolkienCharacter tolkienCharacter : fellowshipOfTheRing) {
names.add(tolkienCharacter.getName());
}
// ... and finally assert something
assertThat(names).contains("Boromir", "Gandalf", "Frodo", "Legolas");
Hey ! It is too much work to extract fields and/or properties by hand. Let AssertJ help you taking care of that. Here is how to check the names of the TolkienCharacter elements in the fellowshipOfTheRing list:
// "name" needs to be either a property or a field of the TolkienCharacter class
assertThat(fellowshipOfTheRing).extracting("name")
.contains("Boromir", "Gandalf", "Frodo", "Legolas")
.doesNotContain("Sauron", "Elrond");
But there is more ! You can extract several fields/properties and check them using tuples. As an example, let's check the name, age and race's name of each TolkienCharacter element :
// when checking several properties/fields you have to use tuples :
import static org.assertj.core.api.Assertions.tuple;
// extracting name, age and and race.name nested property
assertThat(fellowshipOfTheRing).extracting("name", "age", "race.name")
.contains(tuple("Boromir", 37, "Man"),
tuple("Sam", 38, "Hobbit"),
tuple("Legolas", 1000, "Elf"));
The extracted name, age and race's name values of the current element are grouped in a tuple, thus you need to use tuples for expected values.
More examples are available in the test method iterable_assertions_on_extracted_property_values_example in IterableAssertionsExamples.java of the assertj-examples project.
Note that extracting one property can be made strongly typed by giving the property type as the second argument.
// to have type safe extracting, use the second parameter to pass the expected property type:
assertThat(fellowshipOfTheRing).extracting("name", String.class)
.contains("Boromir", "Gandalf", "Frodo", "Legolas")
.doesNotContain("Sauron", "Elrond");
Flat extracting is hard to explain but easy to understand with an example, so let's see how it works (if you are familiar with functional programming, it's just a flatMap).
You have a Player with a teamMates property returning a List<Player>, you want to assert that it returns the expected players:
Player jordan = ... // initialized with Pippen and Kukoc team mates
Player magic = ... // initialized with Jabbar and Worthy team mates
List<Player> reallyGoodPlayers = list(jordan, magic);
// check all team mates by specifying the teamMates property (Player has a getTeamMates() method):
assertThat(reallyGoodPlayers).flatExtracting("teamMates")
.contains(pippen, kukoc, jabbar, worthy);
// alternatively, you can implement an Extractor to extract the team mates:
assertThat(reallyGoodPlayers).flatExtracting(teamMates)
.contains(pippen, kukoc, jabbar, worthy);
where teamMates is an instance of PlayerTeammatesExtractor:
public class PlayerTeammatesExtractor implements Extractor<Player, List<Player>> {
@Override
public List<Player> extract(Player input) {
return input.getTeamMates();
}
}
Like extracting but instead of extracting properties/fields, it extracts the result of a given method invocation on the elements of the Iterable/Array under test and puts the results into a new Iterable/Array which becomes the object under test.
It allows you to test the results of a method call on elements instead of testing the elements themselves. It's especially useful for classes that do not conform to Java Bean's getter specification (e.g toString() or String status() instead of String getStatus()).
Let's take an example to make things clearer :
// WesterosHouse class has a method: public String sayTheWords()
List<WesterosHouse> greatHouses = new ArrayList<WesterosHouse>();
greatHouses.add(new WesterosHouse("Stark", "Winter is Coming"));
greatHouses.add(new WesterosHouse("Lannister", "Hear Me Roar!"));
greatHouses.add(new WesterosHouse("Greyjoy", "We Do Not Sow"));
greatHouses.add(new WesterosHouse("Baratheon", "Our is the Fury"));
greatHouses.add(new WesterosHouse("Martell", "Unbowed, Unbent, Unbroken"));
greatHouses.add(new WesterosHouse("Tyrell", "Growing Strong"));
// let's verify the words of the great houses of Westeros:
assertThat(greatHouses).extractingResultOf("sayTheWords")
.contains("Winter is Coming", "We Do Not Sow", "Hear Me Roar")
.doesNotContain("Lannisters always pay their debts");
Using soft assertions, AssertJ collects all assertion errors instead of stopping at the first one.
So, assuming something goes awry at your dinner party, when using "standard" assertions, this test ...
@Test
public void host_dinner_party_where_nobody_dies() {
Mansion mansion = new Mansion();
mansion.hostPotentiallyMurderousDinnerParty();
assertThat(mansion.guests()).as("Living Guests").isEqualTo(7);
assertThat(mansion.kitchen()).as("Kitchen").isEqualTo("clean");
assertThat(mansion.library()).as("Library").isEqualTo("clean");
assertThat(mansion.revolverAmmo()).as("Revolver Ammo").isEqualTo(6);
assertThat(mansion.candlestick()).as("Candlestick").isEqualTo("pristine");
assertThat(mansion.colonel()).as("Colonel").isEqualTo("well kempt");
assertThat(mansion.professor()).as("Professor").isEqualTo("well kempt");
}
... will yield the less-than-ideal exception message :
org.junit.ComparisonFailure: [Living Guests] expected:<[7]> but was<[6]>
Using soft assertions you can collect all failed assertions:
@Test
public void host_dinner_party_where_nobody_dies() {
Mansion mansion = new Mansion();
mansion.hostPotentiallyMurderousDinnerParty();
// use SoftAssertions instead of direct assertThat methods
SoftAssertions softly = new SoftAssertions();
softly.assertThat(mansion.guests()).as("Living Guests").isEqualTo(7);
softly.assertThat(mansion.kitchen()).as("Kitchen").isEqualTo("clean");
softly.assertThat(mansion.library()).as("Library").isEqualTo("clean");
softly.assertThat(mansion.revolverAmmo()).as("Revolver Ammo").isEqualTo(6);
softly.assertThat(mansion.candlestick()).as("Candlestick").isEqualTo("pristine");
softly.assertThat(mansion.colonel()).as("Colonel").isEqualTo("well kempt");
softly.assertThat(mansion.professor()).as("Professor").isEqualTo("well kempt");
// Don't forget to call SoftAssertions global verification !
softly.assertAll();
}
When the collected assertions are all asserted together they yield a more descriptive error message:
org.assertj.core.api.SoftAssertionError:
The following 4 assertions failed:
1) [Living Guests] expected:<[7]> but was:<[6]>
2) [Library] expected:<'[clean]'> but was:<'[messy]'>
3) [Candlestick] expected:<'[pristine]'> but was:<'[bent]'>
4) [Professor] expected:<'[well kempt]'> but was:<'[bloodied and dishevelled]'>
AssertJ also provides a few ways to avoid having to call softly.assertAll() manually:
JUnitSoftAssertions example:
@Rule
public final JUnitSoftAssertions softly = new JUnitSoftAssertions();
@Test
public void host_dinner_party_where_nobody_dies() {
Mansion mansion = new Mansion();
mansion.hostPotentiallyMurderousDinnerParty();
// use SoftAssertions instead of direct assertThat methods
softly.assertThat(mansion.guests()).as("Living Guests").isEqualTo(7);
softly.assertThat(mansion.kitchen()).as("Kitchen").isEqualTo("clean");
softly.assertThat(mansion.library()).as("Library").isEqualTo("clean");
softly.assertThat(mansion.revolverAmmo()).as("Revolver Ammo").isEqualTo(6);
softly.assertThat(mansion.candlestick()).as("Candlestick").isEqualTo("pristine");
softly.assertThat(mansion.colonel()).as("Colonel").isEqualTo("well kempt");
softly.assertThat(mansion.professor()).as("Professor").isEqualTo("well kempt");
// No need to call softly.assertAll(), it is automatically done by JUnitSoftAssertions rule
}
AutoCloseableSoftAssertions example:
@Test
public void host_dinner_party_where_nobody_dies() {
Mansion mansion = new Mansion();
mansion.hostPotentiallyMurderousDinnerParty();
try (AutoCloseableSoftAssertions softly = new AutoCloseableSoftAssertions()) {
softly.assertThat(mansion.guests()).as("Living Guests").isEqualTo(7);
softly.assertThat(mansion.kitchen()).as("Kitchen").isEqualTo("clean");
softly.assertThat(mansion.library()).as("Library").isEqualTo("clean");
softly.assertThat(mansion.revolverAmmo()).as("Revolver Ammo").isEqualTo(6);
softly.assertThat(mansion.candlestick()).as("Candlestick").isEqualTo("pristine");
softly.assertThat(mansion.colonel()).as("Colonel").isEqualTo("well kempt");
softly.assertThat(mansion.professor()).as("Professor").isEqualTo("well kempt");
// no need to call assertAll, it is done when softly is closed.
}
}
assertSoftly example:
@Test
public void host_dinner_party_where_nobody_dies() {
Mansion mansion = new Mansion();
mansion.hostPotentiallyMurderousDinnerParty();
SoftAssertions.assertSoftly(softly -> {
softly.assertThat(mansion.guests()).as("Living Guests").isEqualTo(7);
softly.assertThat(mansion.kitchen()).as("Kitchen").isEqualTo("clean");
softly.assertThat(mansion.library()).as("Library").isEqualTo("clean");
softly.assertThat(mansion.revolverAmmo()).as("Revolver Ammo").isEqualTo(6);
softly.assertThat(mansion.candlestick()).as("Candlestick").isEqualTo("pristine");
softly.assertThat(mansion.colonel()).as("Colonel").isEqualTo("well kempt");
softly.assertThat(mansion.professor()).as("Professor").isEqualTo("well kempt");
// no need to call assertAll, it is done by assertSoftly.
});
}
Note that in TestNG it is not possible to make the global verification in an @AfterMethod method because if it throws an exception, TestNG skips all subsequent tests.
File assertions are rather poor for checking the file content, so we had the idea to reuse AssertJ String assertions on the content of a file.
See the example below :
File xFile = writeFile("xFile", "The Truth Is Out There");
// classic File assertions
assertThat(xFile).exists().isFile().isRelative();
// String assertions on the file content : contentOf() comes from Assertions.contentOf static import
assertThat(contentOf(xFile)).startsWith("The Truth").contains("Is Out").endsWith("There");
Note that this is meant to be used with small files only, as the whole content is loaded into memory.
How to assert that an exception has been thrown and check that it is the expected one ?
With Java 8 (AssertJ 3.x) :
Testing assertions in Java 8 is elegant, use assertThatThrownBy(ThrowingCallable) to capture and then assert on a Throwable, ThrowingCallable being a functional interface it can be expressed by a lambda.
Example :
@Test
public void testException() {
assertThatThrownBy(() -> { throw new Exception("boom!"); }).isInstanceOf(Exception.class)
.hasMessageContaining("boom");
}
An alternative syntax is provided as some people find it more natural :
@Test
public void testException() {
assertThatExceptionOfType(IOException.class).isThrownBy(() -> { throw new IOException("boom!"); })
.withMessage("%s!", "boom")
.withMessageContaining("boom")
.withNoCause();
}
This later syntax has been enriched for common exceptions :
The previous test can be rewritten as:
@Test
public void testException() {
assertThatIOException().isThrownBy(() -> { throw new IOException("boom!"); })
.withMessage("%s!", "boom")
.withMessageContaining("boom")
.withNoCause();
}
You can test that a piece of code does not throw any exception:
assertThatCode(() -> {
// code that should throw an exception
...
}).doesNotThrowAnyException();
Finally, BDD aficionados can separate when and then steps by using catchThrowable(ThrowingCallable) to capture the throwable and then perform assertions.
Example :
@Test
public void testException() {
// given some preconditions
// when
Throwable thrown = catchThrowable(() -> { throw new Exception("boom!"); });
// then
assertThat(thrown).isInstanceOf(Exception.class)
.hasMessageContaining("boom");
}
With Java 7 (AssertJ 2.x) :
Asserting on exceptions is not as nice compared to the Java 8 way, this is how you would do it in AssertJ 2.x :
Note that fail method can be statically imported from Assertions class
Example taken from FailUsageExamples.java in assertj-examples.
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
import static org.assertj.core.api.Assertions.failBecauseExceptionWasNotThrown;
// ... code omitted for brevety
assertThat(fellowshipOfTheRing).hasSize(9);
// here's the typical pattern to use Fail :
try {
fellowshipOfTheRing.get(9); // argggl !
// we should not arrive here => use fail to expresses that
// if IndexOutOfBoundsException was not thrown, test would fail the specified message
fail("IndexOutOfBoundsException expected because fellowshipOfTheRing has only 9 elements");
} catch (IndexOutOfBoundsException e) {
assertThat(e).hasMessage("Index: 9, Size: 9");
}
// Warning : don't catch Throwable as it would also catch the AssertionError thrown by fail method
// another way to do the same thing
try {
fellowshipOfTheRing.get(9); // argggl !
// if IndexOutOfBoundsException was not thrown, test would fail with message :
// "Expected IndexOutOfBoundsException to be thrown"
failBecauseExceptionWasNotThrown(IndexOutOfBoundsException.class);
} catch (IndexOutOfBoundsException e) {
assertThat(e).hasMessage("Index: 9, Size: 9");
}
Sometime you want to compare objects with a strategy not relying on the equals method, this is possible in AssertJ thanks to two methods :
usingComparator(Comparator) example :
// frodo and sam are instances of TolkienCharacter with Hobbit race (obviously :)), they are not equal ...
assertThat(frodo).isNotEqualTo(sam);
// ... but if we compare race only, they are (raceComparator implements Comparator<TolkienCharacter>)
assertThat(frodo).usingComparator(raceComparator).isEqualTo(sam);
usingElementComparator(Comparator) example :
// standard comparison : the fellowshipOfTheRing includes Gandalf but not Sauron (believe me) ...
assertThat(fellowshipOfTheRing).contains(gandalf).doesNotContain(sauron);
// ... but if we compare race only, Sauron is in fellowshipOfTheRing (he's a Maia like Gandalf)
assertThat(fellowshipOfTheRing).usingElementComparator(raceComparator).contains(sauron);
This is a special case of custom comparison where objects are compared "by value", the comparison is performed on all fields/properties, several variants are provided :
Except isEqualToComparingFieldByFieldRecursively, the comparison performed is not recursive, i.e. if one of the field is an Object, it will be compared to the other field using its equals method (whether equals has been overriden or not). On the other hand isEqualToComparingFieldByFieldRecursively compares Object fields with a field by field strategy unless it has a custom equals implementation (i.e. the overriden equals method will be used instead of a field by field comparison).
You can specify a custom comparison strategy by field type or for a given group of fields, this is useful when comparing doubles or floats as shown at the end of isEqualToComparingFieldByFieldRecursively example.
Examples :
In the following examples, the TolkienCharacter class has getters but its equals method has not been overridden.
isEqualToComparingFieldByField :
TolkienCharacter frodo = new TolkienCharacter("Frodo", 33, HOBBIT);
TolkienCharacter frodoClone = new TolkienCharacter("Frodo", 33, HOBBIT);
// Fail as equals compares object references
assertThat(frodo).isEqualsTo(frodoClone);
// frodo and frodoClone are equal when doing a field by field comparison.
assertThat(frodo).isEqualToComparingFieldByField(frodoClone);
isEqualToComparingOnlyGivenFields :
TolkienCharacter frodo = new TolkienCharacter("Frodo", 33, HOBBIT);
TolkienCharacter sam = new TolkienCharacter("Sam", 38, HOBBIT);
// frodo and sam both are hobbits, so they are equal when comparing only race
assertThat(frodo).isEqualToComparingOnlyGivenFields(sam, "race"); // OK
// they are also equals when comparing only race name (nested field).
assertThat(frodo).isEqualToComparingOnlyGivenFields(sam, "race.name"); // OK
// ... but not when comparing both name and race
assertThat(frodo).isEqualToComparingOnlyGivenFields(sam, "name", "race"); // FAIL
isEqualToIgnoringGivenFields :
TolkienCharacter frodo = new TolkienCharacter("Frodo", 33, HOBBIT);
TolkienCharacter sam = new TolkienCharacter("Sam", 38, HOBBIT);
// frodo and sam are equal when ignoring name and age as the only remaining field is race
assertThat(frodo).isEqualToIgnoringGivenFields(sam, "name", "age"); // OK both are HOBBIT
// ... but they are not equals if only age is ignored as their names differ.
assertThat(frodo).isEqualToIgnoringGivenFields(sam, "age"); // FAIL
isEqualToIgnoringNullFields :
TolkienCharacter frodo = new TolkienCharacter("Frodo", 33, HOBBIT);
TolkienCharacter mysteriousHobbit = new TolkienCharacter(null, 33, HOBBIT);
// Null fields in expected object are ignored, mysteriousHobbit has a null name thus it's ignored
assertThat(frodo).isEqualToIgnoringNullFields(mysteriousHobbit); // OK
// ... but this is not reversible !
assertThat(mysteriousHobbit).isEqualToIgnoringNullFields(frodo); // FAIL
isEqualToComparingFieldByFieldRecursively :
public class Person {
public String name;
public double height;
public Home home = new Home();
public Person bestFriend;
// constructor with name and height omitted for brevity's sake
}
public class Home {
public Address address = new Address();
}
public static class Address {
public int number = 1;
}
Person jack = new Person("Jack", 1.80);
jack.home.address.number = 123;
Person jackClone = new Person("Jack", 1.80);
jackClone.home.address.number = 123;
// cycle are handled in comparison
jack.bestFriend = jackClone;
jackClone.bestFriend = jack;
// will fail as equals compares object references
assertThat(jack).isEqualsTo(jackClone);
// jack and jackClone are equal when doing a recursive field by field comparison
assertThat(jack).isEqualToComparingFieldByFieldRecursively(jackClone);
// any type/field can be compared with a a specific comparator.
// let's change jack's height a little bit
jack.height = 1.81;
// assertion fails because of the height difference
// (the default precision comparison for double is 1.0E-15)
assertThat(jack).isEqualToComparingFieldByFieldRecursively(jackClone);
// use usingComparatorForType to specify how to compare the given type
// assertion succeeds because we allow a 0.5 tolerance on double
assertThat(jack).usingComparatorForType(new DoubleComparator(0.5), Double.class)
.isEqualToComparingFieldByFieldRecursively(jackClone);
// use usingComparatorForFields to specify how to compare some fields (nested fields are supported)
assertThat(jack).usingComparatorForFields(new DoubleComparator(0.5), "height")
.isEqualToComparingFieldByFieldRecursively(jackClone);
Assertions error messages use a Representation to format different types involved. There are multiple ways of registering a custom representation for assertions:
Examples:
private class Example {} // dummy class
public class CustomRepresentation extends StandardRepresentation {
// override needed to hook non predefined type formatting
@Override
public String fallbackToStringOf(Object o) {
if (o instanceof Example) return "Example";
// fallback to default formatting.
return super.fallbackToStringOf(o);
}
// override a predefined type formatting : String
@Override
protected String toStringOf(String str) {
return "$" + str + "$";
}
}
Global scope custom representation (default on startup) :
You will need to register your Representation as an SPI. You need to do the following for that:
//You don't need to do anything, AssertJ will pick up the Representation automatically
// this assertion fails ...
assertThat(new Example()).isNull();
// ... with error :
expected:<[null]> but was:<[Example]>
// this one fails ...
assertThat("foo").startsWith("bar");
// ... with error :
Expecting:
<$foo$>
to start with:
<$bar$>
Global scope custom representation (after call) :
// register CustomRepresentation only once. However, all tests before calling this would use the default one
Assertions.useRepresentation(new CustomRepresentation());
// this assertion fails ...
assertThat(new Example()).isNull();
// ... with error :
expected:<[null]> but was:<[Example]>
// this one fails ...
assertThat("foo").startsWith("bar");
// ... with error :
Expecting:
<$foo$>
to start with:
<$bar$>
Per assertion scope custom representation :
// we need to register CustomRepresentation for each assertions
Representation customRepresentation = new CustomRepresentation();
// this assertion fails ...
assertThat(new Example()).withRepresentation(customRepresentation)
.isNull();
// ... with error :
expected:<[null]> but was:<[Example]>
// this one fails ...
assertThat("foo").withRepresentation(customRepresentation)
.startsWith("bar");
// ... with error :
Expecting:
<$foo$>
to start with:
<$bar$>