DEV Community

André Schreck
André Schreck

Posted on

Writing readable tests with AssertJ

AssertJ is a library that provides "fluent assertions for java". The fluent interface of the assertions allows developers to write very readable tests. In this article I will try to show how this can be accomplished for your own domain objects.
Let's imagine there is some kind of Validator that returns a ValidationResult. The ValidationResult could look like that:

public enum ErrorCause {
    PARSE,
    RUNTIME
}

public class ValidationResult {

    private final boolean failed;
    private final ErrorCause errorCause;
    private final String message;

    public ValidationResult(boolean failed, ErrorCause errorCause, String message) {
        this.failed = failed;
        this.errorCause = errorCause;
        this.message = message;
    }

    public boolean hasFailed() {
        return failed;
    }

    public ErrorCause getErrorCause() {
        return errorCause;
    }

    public String getMessage() {
        return message;
    }
}

When testing the validator the ValidationResult will be checked according to the expectations of the test. One way to describe the assertions with AssertJ is the following.

ValidationResult result = validator.validateSomething(...);
assertThat(result.hasFailed()).isTrue();
assertThat(result.getErrorCause()).isEqualTo(ErrorCause.PARSE);
assertThat(result.getMessage()).isEqualTo("the expected message");

If done like that, you do not really win something in comparison to other assertion libraries.
It is possible to have the assertion automatically generated. That would make the test code look like that.

ValidationResult result = validator.validateSomething(...);
assertThat(result).isFailed().hasErrorCause(ErrorCause.PARSE).hasMessage("the expected message");

The problem here is, that the readability can still be improved.
AssertJ lets you write your own Assert classes for your domain objects. That means all the methods can be named the way that really readable tests can be produced. The class ValidationResultAssert could look like that.

public class ValidationResultAssert extends AbstractAssert<ValidationResultAssert, ValidationResult> {

    private  ValidationResultAssert(ValidationResult actual) {
        super(actual, ValidationResultAssert.class);
    }

    public ValidationResultAssert hasFailed() {
        Assertions.assertThat(actual.hasFailed()).isTrue();
        return this;
    }

    public ValidationResultAssert dueToAParsingError() {
        Assertions.assertThat(actual.getErrorCause()).isEqualTo(ErrorCause.PARSE);
        return this;
    }

    public ValidationResultAssert dueToCauseARuntimeError() {
        Assertions.assertThat(actual.getErrorCause()).isEqualTo(ErrorCause.RUNTIME);
        return this;
    }

    public ValidationResultAssert withTheMessage(String message) {
        Assertions.assertThat(actual.getMessage()).isEqualTo(message);
        return this;
    }

    public static ValidationResultAssert assertThat(ValidationResult actual) {
        return new ValidationResultAssert(actual);
    }
}

This leads to a assertion that can be read like a sentence, which makes it very easy to understand why the expectations in the test are the way they are. In addition to that a test like that also provides an explicit description of the dependencies within you domain model.

assertThat(result)
    .hasFailed()
    .dueToAParsingError()
    .withTheMessage("the expected message");

This is is a rather simple example, but as your code's complexity grows the readability of your tests gets more and more important.

Top comments (1)

Collapse
 
ryuuji159 profile image
Daniel Cortés

I really liked the library, I will start using it with my project s <3