AssertJ Assertions Generator

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 :

  • boolean :
    • isX()
    • isNotX()
  • int, long, short, byte, char :
    • hasX(value) - values compared with ==
  • float and double :
    • hasX(value) - values compared with ==
    • hasXCloseTo(value, delta), diff < delta check
  • Iterable<T> and T[] :
    • hasX(T... values)
    • hasOnlyX(T... values)
    • doesNotHaveX(T... values)
    • hasNoX()
    Element comparison is based on the T::equals method.
  • Object :
    • hasX(value) - equals based comparison
Notes

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.

Assertions method templates
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")
Assertions class templates
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
Assertions entry point templates
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.

  • Allow to generate assertions for all fields whatever their visibility is.
  • Generate BDD Soft assertions.
  • Generate JUnit BDD Soft assertions.
  • Generate AutoCloseableSoftAssertions.
  • For iterable properties, generate hasXXXs, hasOnlyXXXs and doesNotHaveXXXs taking a collection argument.
  • Allow to set in which package assertions are generated.
  • Allow to generate assertions for annotated methods or classes with GenerateAssertion.
  • Add @javax.annotation.Generated to generated classes.
  • Generated assertions now extend AbstractObjectAssert providing common object assertions.
  • Make generated assertions extend AbstractComparableAssert when possible providing comparable assertions.
  • Generate assertions methods with @CheckReturnValue to detect incorrect usage.
  • Play more friendly with post instrumentalization tool like jacoco.
  • Fix : avoid potential clash with offset parameter by renaming it to assertjOffset.
  • Fix : generate correct file name when assertions entry point template class name is changed.
  • Fix : could not find predicate methods that collide.
  • Fix : some javadoc tags were not correct.
  • Fix : parent class import was missing when it was not in same package as its child.
  • Fix : explicitly exclude package-info classes from being processed for assertion generation.
  • Fix : consistently avoid generating assertions for non public classes.
  • custom_abstract_assertion_class_template.txt: add ${imports}.
  • All has_assertion_xxx templates: get${Property}() changed to actual.${getter}()
  • All has_elements_xxx templates: get${Property}() changed to actual.${getter}()

Release date : 2015-08-01

Thanks to Alexander Bischof for his contributions to this release.

  • Uses java 7.
  • Define different assertion templates for whole number, real numbers and char properties.
  • Document how assertion templates are used to generate assertions.
  • Generate both hasValue(v) and hasValueCloseTo(v, delta) for real numbers properties.
  • Fix : Predicate assertion was badly generated when boolean property contained 'is'.
  • Fix : Predicate assertion is now generated for Boolean property not starting by 'is'.

Release date : 2015-04-11

Huge thanks to Jeremy Krieg for his contributions to this release.

  • Generate hasOnly and doesNotHave assertions for array/iterable properties.
  • Better error messages.
  • Sanitise property names when they clash with Java keywords. (Fr Jeremy Krieg)
  • Skip generation of field assertion if it would cause a name clash with a property assertion. (Fr Jeremy Krieg)
  • Handle other boolean predicate forms than is like was, can, will, shouldBe. (Fr Jeremy Krieg)
  • Generated iterable/array assertions did not to honour the description set with as() method when given iterable/array was null.

Release date : 2014-11-02

  • Generate JUnitSoftAssertions.
  • Fix potential import clash by using fully qualified class names (except for AssertJ classes).
  • Fix class cast exception when an overriden getter returns a generic type.

Release date : 2014-08-31

  • Fixes bad generated code in hasNoXXX assertion for iterable property.
  • Fixes bad generated code in contains assertion for iterable property with upper bound wildcard (? extends).
  • Fix to generate assertion for classes having '$' character in class name.
  • Fix fenerated assertion for public boolean field.
  • Use fully qualified name class instead of import in Assertions entry point classes.

Release date : 2014-08-17

  • Correctly resolve generic return types.
  • Use Guava ClassPath instead of org.reflections.

Release date : 2014-08-03

Big thanks to Fr Jeremy Krieg for his work on this release.

  • Generate assertions using offset for double and float types.
  • Allow to generate assertion classes that don't hide subclasses assertions after using an assertion from a super class. (Fr Jeremy Krieg)
  • Generate assertions for public fields.
  • Prepend enclosing class name to inner class assertion name to avoid clash when several inner classes have the same name.
  • Don't generate classes with unused import. (Fr Jeremy Krieg)
  • Generated javadoc improvements. (Fr Jeremy Krieg)
  • Allow to specify where to generate Assertions entry point classes. (floppywaste)
  • Generate a BDD style entry point class BddAssertions replacing assertThat methods by then.
  • Generate a SoftAssertions entry point class to collect all assertion errors instead of stopping at the first one.

Release date : 2013-12-08

  • To ease using generated assertions, generate an entry point class Assertions providing assertThat methods giving access to each generated *Assert classes.
  • Don't generate hasDeclaringClass assertion for Enum.
  • In generated method assertions, rename errorMessage local variable to assertjErrorMessage to avoid collision when object under test has an errorMessage property. (William Delanoue)

Release date : 2013-09-15

Big thanks to Fabien Duminy, he has done most of the work of this release !

  • Generated assertions use null safe equals for non primitive types.
  • Add exceptions to assertion method signature if underlying getter of class to assert throws some. (Fabien Duminy)
  • Generate assertions for nested/inner classes. (Fabien Duminy)
  • Fix error message displaying unrelevant property value when getter of class to assert doesn't return the same value on successive calls. (Fabien Duminy)

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:

  • Fabien Duminy
  • William Delanoue
  • floppywaste
  • Fr Jeremy Krieg