Use the Assertion Generator to create assertions specific to your own classes, either through a simple command line tool, a maven plugin or a gradle plugin.
Having assertions specific to your own classes allows you to use the domain model vocabulary in your test assertions, making them easier to read and closer to the problem you are trying to solve. It's a way to apply Domain Driven Design ubiquitous language in tests.
Let's say you have a Player class with name, team and teamMates properties (also works with public fields).
public class Player {
private String name;
private String team;
private List<String> teamMates;
// constructor and setters omitted
public String getName() {
return name;
}
public String getTeam() {
return team;
}
public List<String> getTeamMates() {
return teamMates;
}
@Override
public String toString() {
return "Player[name="+name+"]";
}
}
The generator is able to create a PlayerAssert assertion class with a hasName and a hasTeam assertion which can be used like this :
assertThat(mj).hasName("Michael Jordan")
.hasTeam("Chicago Bulls")
.hasTeamMates("Scottie Pippen", "Tony Kukoc");
The error message of a failed assertion reflects the property checked. For example if hasName fails you will get this error message :
Expecting name of:
<Player[name=Air Jordan]>
to be:
<Michael Jordan>
but was:
<Air Jordan>
The goal of the generator is to lower the barrier to use custom assertions. In our opinion it is just a starting point to get domain specific assertions. Some people will be satisfied with the generated code, other will modify it to get better wording or additional assertions and put the code in source control as test utilities classes.
The generator has its limitations and is sometimes not able to generate code that is 100% compilation errors free (but don't worry most of the time it does !). We don't see this as a blocker because if 99% of the generated code is correct and the remaining is easy to fix, then we consider the goal to be achieved since you were able to get a lot custom assertions with limited effort. If you need 100% correct generated code then you can exclude the offending classes from having assertions being generated or even better contribute to fix the generator !
The generator creates assertions for each property and public field. The assertions created are specific to the property/field type. The following list shows which assertions are created for a given type :
The generator introspects properties and public fields (although it can include private fields too with the generateAssertionsForAllFields option).
The same assertions are generated for primitive types and their wrapper versions (ex: double and Double), the only difference being the performed check : null safe equals comparison for wrappers and == for primitives.
You can change what is generated by modifying the templates used to generate assertions, templates are located in the templates directory.
Let's enrich our Player with different properties/public fields to see what assertions are generated :
public class Player {
// no getter needed to generate assertion for public fields
// private fields getters and setters omitted for brevity
public String name; // Object assertion generated
private int age; // whole number assertion generated
private double height; // real number assertion generated
private boolean retired; // boolean assertion generated
private List<String> teamMates; // Iterable assertion generated
}
Created assertions :
public class PlayerAssert extends AbstractObjectAssert<PlayerAssert, Player> {
// javadoc and implementation omitted for brevity !
public PlayerAssert hasAge(int age) { ... }
public PlayerAssert hasHeight(double height) { ... }
public PlayerAssert hasHeightCloseTo(double height, double offset) { ... }
public PlayerAssert hasTeamMates(String... teamMates) { ... }
public PlayerAssert hasOnlyTeamMates(String... teamMates) { ... }
public PlayerAssert doesNotHaveTeamMates(String... teamMates) { ... }
public PlayerAssert hasNoTeamMates() { ... }
public PlayerAssert isRetired() { ... }
public PlayerAssert isNotRetired() { ... }
public PlayerAssert hasName(String name) { ... }
public PlayerAssert(Player actual) {
super(actual, PlayerAssert.class);
}
public static PlayerAssert assertThat(Player actual) {
return new PlayerAssert(actual);
}
}
The generator will also create entry point classes like Assertions, BddAssertions and SoftAssertions/JUnitSoftAssertions to ease accessing each generated *Assert class.
Let's say for example that the generator has been applied on the Player and Game classes. It will have created PlayerAssert and GameAssert classes and an Assertions class looking like this :
// the generator selects the base package of classes it generates assertions for
package org.player;
public class Assertions {
/**
* Creates a new instance of <code>{@link GameAssert}</code>.
*
* @param actual the actual value.
* @return the created assertion object.
*/
public static GameAssert assertThat(Game actual) {
return new GameAssert(actual);
}
/**
* Creates a new instance of <code>{@link PlayerAssert}</code>.
*
* @param actual the actual value.
* @return the created assertion object.
*/
public static PlayerAssert assertThat(Player actual) {
return new PlayerAssert(actual);
}
/**
* Creates a new <code>{@link Assertions}</code>.
*/
protected Assertions() {
// empty
}
}
Now you just have to statically import org.player.Assertions.assertThat to use custom assertions for Player and Game in your tests :
import static org.player.Assertions.assertThat;
...
assertThat(mj).hasName("Michael Jordan")
.hasTeam("Chicago Bulls")
.hasTeamMates("Scottie Pippen", "Tony Kukoc");
The assertion generator uses templates to create assertions, templates are located in the templates directory. There is a template for each type and templates for assert class and entry points.
You can (and are encouraged to) modify the templates to generate additional or different assertions, but be sure to generate code that compiles ! :)
The templates contain variables describing the properties, fields and classes for which assertions are generated. These variables are replaced by the actual values read for each class we generate assertions for.
Let's see how it works with the template used to generate hasName(String name) assertion for Player.
has_assertion_template.txt :
/**
* Verifies that the actual ${class_to_assert}'s ${property} is equal to the given one.
* @param ${property_safe} the ${property} to compare the actual ${class_to_assert}'s ${property} to.
* @return this assertion object.
* @throws AssertionError - if actual ${class_to_assert}'s ${property} is not equal to the given one.${throws_javadoc}
*/
public ${self_type} has${Property}(${propertyType} ${property_safe}) ${throws}{
// check that actual ${class_to_assert} we want to make assertions on is not null.
isNotNull();
// overrides the default error message with a more explicit one
String assertjErrorMessage = "\nExpecting ${property} of:\n <%s>\nto be:\n <%s>\nbut was:\n <%s>";
// null safe check
${propertyType} actual${Property} = actual.get${Property}();
if (!Objects.areEqual(actual${Property}, ${property_safe})) {
failWithMessage(assertjErrorMessage, actual, ${property_safe}, actual${Property});
}
// return the current assertion for method chaining
return ${myself};
}
Variables used to generate hasName(String name) assertion :
Variable | Usage | Example |
---|---|---|
${property} | property/field we generate an assertion for | name |
${propertyType} | The type of the property/field we generate an assertion for | String |
${Property} | property/field we generate an assertion for, with a capital letter | Name |
${property_safe} | ${property} if not equal to a reserved java keyword, expected${Property} otherwise | name |
${throws} | the exceptions thrown by the property getter (if any) | - |
${throws_javadoc} | javadoc of the exceptions thrown by the property getter (if any) | - |
${class_to_assert} | Class we are generating assertion for | Player |
${self_type} | the assertion class name | PlayerAssert |
${myself} | represents the assertion class instance used to perform assertions | this |
And finally here's the generated assertion :
/**
* Verifies that the actual Player's name is equal to the given one.
* @param name the name to compare the actual Player's name to.
* @return this assertion object.
* @throws AssertionError - if the actual Player's name is not equal to the given one.
*/
public PlayerAssert hasName(String name) {
// check that actual Player we want to make assertions on is not null.
isNotNull();
// overrides the default error message with a more explicit one
String assertjErrorMessage = "\nExpecting name of:\n <%s>\nto be:\n <%s>\nbut was:\n <%s>";
// null safe check
String actualName = actual.getName();
if (!Objects.areEqual(actualName, name)) {
failWithMessage(assertjErrorMessage, actual, name, actualName);
}
// return the current assertion for method chaining
return this;
}
Here's the list of template files and what they are used for.
Assertion method template file | Usage | Example |
---|---|---|
has_assertion_template.txt | assertion for Object (used unless a more specific template is found) | hasName("Joe") |
is_assertion_template.txt | assertion for boolean | isRookie() |
is_wrapper_assertion_template.txt | assertion for Boolean | isRookie() |
has_assertion_template_for_char.txt | assertion for char primitive type | hasGender('F') |
has_assertion_template_for_character.txt | assertion for Character | hasGender('F') |
has_assertion_template_for_whole_number.txt | assertion for int, long, short and byte | hasAge(23) |
has_assertion_template_for_whole_number_wrapper.txt | assertion for Integer, Long, Short and Byte | hasAge(23) |
has_assertion_template_for_real_number.txt | assertion for float and double | hasHeight(1.87) |
has_assertion_template_for_real_number_wrapper.txt | assertion for Float and Double | hasHeight(1.87) |
has_elements_assertion_template_for_array.txt | assertion for array. | hasTeamMates("mj") |
has_elements_assertion_template_for_iterable.txt | assertion for Iterable. | hasTeamMates("mj") |
Assertion class template file | Usage |
---|---|
custom_assertion_class_template.txt | assert class skeleton |
custom_abstract_assertion_class_template.txt | base assertion class in in hierarchical assertion |
custom_hierarchical_assertion_class_template.txt | concrete assertion class in in hierarchical assertion |
Entry point class/methods template file | Usage |
---|---|
standard_assertions_entry_point_class_template.txt | Assertions class skeleton |
standard_assertion_entry_point_method_template.txt | template for assertThat method in entry point class like Assertions |
bdd_assertions_entry_point_class_template.txt | BddAssertions class skeleton |
bdd_assertion_entry_point_method_template.txt | template for then method in BddAssertions entry point class |
soft_assertions_entry_point_class_template.txt | SoftAssertions class skeleton |
soft_assertion_entry_point_method_template.txt | template for "soft" assertThat method in SoftAssertions entry point class |
junit_soft_assertions_entry_point_class_template.txt | JUnitSoftAssertions class skeleton |
Variables used in the assertions templates:
Common variables (assertion level) | ||
---|---|---|
${property} | property/field we generate an assertion for | |
${propertyType} | The type of the property/field we generate an assertion for | |
${Property} | property/field we generate an assertion for, with a capital letter | |
${property_safe} | ${property} if not equal to a reserved java keyword, expected${Property} otherwise | |
${throws} | the exceptions thrown by the property getter (if any) | |
${throws_javadoc} | javadoc of the exceptions thrown by the property getter (if any) | |
${class_to_assert} | Class we are generating assertion for | |
${self_type} | the assertion class name | |
${myself} | represents the assertion class instance used to perform assertions | |
Variables specific to boolean/predicate assertions | ||
${predicate} | predicate assertion method name. ex: isRookie() | |
${neg_predicate} | negative predicate assertion method name. ex: isNotRookie() | |
${predicate_for_error_message_part1} | used to build the first part of a predicate error message. | |
${predicate_for_error_message_part2} | used to build the second part of a predicate error message. | |
${negative_predicate_for_error_message_part1} | used to build the first part of a negative predicate error message. | |
${negative_predicate_for_error_message_part2} | used to build the second part of a negative predicate error message. | |
${predicate_for_javadoc} | predicate assertion method javadoc | |
${negative_predicate_for_javadoc} | negative predicate assertion method javadoc | |
Variables specific to array assertions | ||
${elementType} | the element type of the array property. ex: String for a String[] property | |
Variables specific to assertion class | ||
${custom_assertion_class} | the assertion class name. ex: PlayerAssert | |
${super_assertion_class} | used for the super class in hierarchical assertions. | |
${imports} | imports off the current generated assertion class. | |
${package} | assertion class package (same as the class we generate assertion for). | |
Variables specific to assertion entry points classes | ||
${all_assertions_entry_points} | all the generated entry points. ex: assertThat for Assertions or then for BddAssertions |
There are a few cases where the generator is not able to generate code that is 100% correct, notably for some generic types that expose generic fields or properties like FHIR PrimitiveType.
In that case, you can exclude the offending classes from having assertions being generated or fix the generated code manually (and put it in source control).
As a reminder, the philosophy of the generator is to lower the barrier to have custom assertions, if 99% of the generated code is correct and the remaining is easy to fix, then we consider the job done because you were able to get a lot custom assertions with limited effort..
The generator does not handle generics in types it creates assertions for, we have tried (hard!) adding generics support, it turns out to be a really complex task especially handling correctly generics bounds. We came to a 90% support but the remaining 10% were difficult to solve and it made the code brittle overall. The work in progress is in generics-support branch for anyone who wants to give it a go.
This quickstart guide is for non maven users, if you are a maven user, you should use the assertions generator maven plugin.
Gradle users can use the gradle plugin or look at this gradle build script to run the generator from Gradle.
Get the AssertJ Assertions Generator archive (either for unix or windows) and extract it, its structure is :
assertion-generator/
|-- lib/
|-- templates/
|-- generate-assertions script (.bat or .sh)
First, put in lib/ the jars containing the classes you want to generate assertions for, then run the script with either the classes or the packages containing the classes you want to generate assertions for.
Example : generating assertions for TolkienCharacter class :
generate-assertions.sh org.assertj.examples.data.TolkienCharacter
Example : generating assertions for all classes in org.assertj.examples.data :
generate-assertions.sh org.assertj.examples.data
Your assertions classes have been generated ! You can find them in the current directory.
You can change what is generated by modifying the templates used for assertions creation (have a look at templates directory)
Release date : 2017-10-01
Thanks to Sami Paavilainen, Pascal Schumacher, Gaƫl Lhez, James Strachan, Brian Norman, Kevin Brightwell and Christian Wiejack for their contributions to this release.
Release date : 2015-08-01
Thanks to Alexander Bischof for his contributions to this release.
Release date : 2015-04-11
Huge thanks to Jeremy Krieg for his contributions to this release.
Release date : 2014-11-02
Release date : 2014-08-31
Release date : 2014-08-17
Release date : 2014-08-03
Big thanks to Fr Jeremy Krieg for his work on this release.
Release date : 2013-12-08
Release date : 2013-09-15
Big thanks to Fabien Duminy, he has done most of the work of this release !
Release date : 2013-03-26
It's the first release after Fest fork, generated assertions are cleaner.
If you have any questions, please use AssertJ google group.
AssertJ assertions generator is hosted on github : https://github.com/joel-costigliola/assertj-assertions-generator.
Please report bugs or missing features in AssertJ assertions generator issue tracker.
Thanks for your interest ! Please check our contributor's guidelines.
Special thanks to AssertJ assertions generator contributors: