Here is my way to unit test my objects with fields annotated with some javax.validation.constraints restrictions.
I will give an example with Java 8, the JPA entity, Spring Boot and JUnit 5, but the general idea is the same regardless of context and frameworks:
We have a nominal scenario in which all fields are correctly evaluated and, as a rule, multiple error scenarios when one or more fields are incorrectly evaluated.
Validating field validation is not particularly difficult. But since we have many fields to test, the tests can be complicated, we can forget some cases by introducing side effects in the tests between the two cases to check or just introducing duplication. I will talk about how to avoid this.
In the OP code, we assume that 3 fields have a NotNull constraint. I think that under three different constraints, the pattern and its meaning are less noticeable.
I wrote unit test first for a nominal scenario:
import org.junit.jupiter.api.Test; @Test public void persist() throws Exception { Contact contact = createValidContact();
I am extracting the code to create a valid contact in the method, as it will be useful without nominal cases:
private Contact createValidContact(){ Contact contact = new Contact(); contact.setEmail("Jackyahoo.com"); contact.setName("Jack"); contact.setPhone("33999999"); return contact; }
Now I am writing a @parameterizedTest as the source source of a @MethodSource :
import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import javax.validation.ConstraintViolationException; @ParameterizedTest @MethodSource("persist_fails_with_constraintViolation_fixture") void persist_fails_with_constraintViolation(Contact contact ) { assertThrows(ConstraintViolationException.class, () -> { contactRepository.save(contact); entityManager.flush(); }); }
To compile / run @parameterizedTest , consider adding the required dependency, which is not included in the junit-jupiter-api dependency:
<dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-params</artifactId> <version>${junit-jupiter.version}</version> <scope>test</scope> </dependency>
In the bind method to create invalid contacts, the idea is simple. For each case, I create a new valid contact object, and I incorrectly set only the field for checking the corresponding one.
Thus, I guarantee that there is no side effect between the cases and that each case causes the expected exception of validation, since without the set field, the actual contact is successfully saved.
private static Stream<Contact> persist_fails_with_constraintViolation_fixture() { Contact contactWithNullName = createValidContact(); contactWithNullName.setName(null); Contact contactWithNullEmail = createValidContact(); contactWithNullEmail.setEmail(null); Contact contactWithNullPhone = createValidContact(); contactWithNullPhone.setPhone(null); return Stream.of(contactWithNullName, contactWithNullEmail, contactWithNullPhone); }
Here is the complete test code:
import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import javax.validation.ConstraintViolationException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; import org.springframework.test.context.junit.jupiter.SpringExtension; @DataJpaTest @ExtendWith(SpringExtension.class) public class ContactRepositoryTest { @Autowired private TestEntityManager entityManager; @Autowired private ContactRepository contactRepository; @BeforeEach public void setup() { entityManager.clear(); } @Test public void persist() throws Exception { Contact contact = createValidContact();