AssertJ custom assertion for ConstraintValidator tests

Posted at — Jan 22, 2017

As a follow-up to my last post on a custom validator to check if a String contains XML, I like to elaborate on how I made the unit test so readable.

To repeat, here is part of the unit test again with the assertions highlighted:

@Test
public void givenNoXml_notValid() {
    ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
    Validator validator = factory.getValidator();

    TestObject testObject = new TestObject("This is no XML string");
    Set<ConstraintViolation<TestObject>> violationSet = validator.validate(testObject);

    assertThat(violationSet).hasViolationOnPath("xml");
}

@Test
public void givenXml_valid() {

    ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
    Validator validator = factory.getValidator();

    TestObject testObject = new TestObject("<Node>test</Node>");
    Set<ConstraintViolation<TestObject>> violationSet = validator.validate(testObject);

    assertThat(violationSet).hasNoViolations();
}

I love using AssertJ for assertions in my unit tests, because:

  • They are very readable

  • They are code completion friendly

  • You don’t have to remember if the expected value goes first or last

AssertJ also support adding custom assertions, which is what I did here:

import org.assertj.core.api.AbstractAssert;

import javax.validation.ConstraintViolation;
import java.util.Set;
import java.util.stream.Collectors;

public class ConstraintViolationSetAssert extends AbstractAssert<ConstraintViolationSetAssert, Set<? extends ConstraintViolation>> {

    public ConstraintViolationSetAssert(Set<? extends ConstraintViolation> actual) {
        super(actual, ConstraintViolationSetAssert.class);
    }

    public static ConstraintViolationSetAssert assertThat(Set<? extends ConstraintViolation> actual) {
        return new ConstraintViolationSetAssert(actual);
    }

    public ConstraintViolationSetAssert hasViolationOnPath(String path) {
        isNotNull();

        // check condition
        if (!containsViolationWithPath(actual, path)) {
            failWithMessage("There was no violation with path <%s>. Violation paths: <%s>", path, actual.stream()
                                                                                                        .map(violation -> violation
                                                                                                                .getPropertyPath()
                                                                                                                .toString())
                                                                                                        .collect(
                                                                                                                Collectors
                                                                                                                        .toList()));
        }

        return this;
    }

    public ConstraintViolationSetAssert hasNoViolations() {
        isNotNull();

        if (!actual.isEmpty()) {
            failWithMessage("Expecting no violations, but there are %s violations", actual.size());
        }

        return this;
    }

    private boolean containsViolationWithPath(Set<? extends ConstraintViolation> violations, String path) {
        boolean result = false;

        for (ConstraintViolation violation : violations) {
            if (violation.getPropertyPath().toString().equals(path)) {
                result = true;
                break;
            }
        }

        return result;
    }
}

The actual process of creating a custom assertion is explained in detail on the AssertJ website.

This know-how originated during the development of a PegusApps project.

If you want to be notified in the future about new articles, as well as other interesting things I'm working on, join my mailing list!
I send emails quite infrequently, and will never share your email address with anyone else.