Search code examples
javaspring-bootjpajunit5data-jpa-test

@DataJpaTest ignores @Column(unique = true) and @Column(nullable = false)


I have an issue in testing save operation by Jpa.

I have a User object:

@Entity
@Table(name = "USERS", schema = "ecommercy")
@EntityListeners(AuditingEntityListener.class)
@JsonIgnoreProperties(value = {"createdAt", "updatedAt"}, allowGetters = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@ToString
public class User {

    @Id
    @GeneratedValue(generator = "uuid2")
    @GenericGenerator(name = "uuid2", strategy = "org.hibernate.id.UUIDGenerator")
    @Column(updatable = false, nullable = false, columnDefinition = "VARCHAR(255)")
    private String id;

    @Column(unique = true, nullable = false)
    private String email;

    @Column(unique = true)
    private String phoneNumber;

    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
    @Column(nullable = false)
    private String password;

    @Column(nullable = false)
    private String name;

    private String lastName;

    @OneToMany(mappedBy = "user")
    @Cascade(org.hibernate.annotations.CascadeType.ALL)
    private List<Address> addresses;

    @OneToOne(mappedBy = "user")
    @Cascade(org.hibernate.annotations.CascadeType.ALL)
    private Basket basket;

    @OneToMany(mappedBy = "user")
    @Cascade(org.hibernate.annotations.CascadeType.ALL)
    private List<Order> orders;

    @Builder.Default
    @Column(nullable = false)
    private int points = 0;

    @OneToMany(mappedBy = "user")
    @Cascade(org.hibernate.annotations.CascadeType.ALL)
    private List<Comment> comments;

    @Builder.Default
    @Convert(converter = StringListConverter.class)
    @Column(updatable = false)
    private List<String> roles = List.of("USER");

    @Builder.Default
    @Temporal(TemporalType.TIMESTAMP)
    @CreatedDate
    @Column(nullable = false, updatable = false)
    private Date createdAt = new Date();

    @Builder.Default
    @Column(nullable = false)
    @Temporal(TemporalType.TIMESTAMP)
    @LastModifiedDate
    private Date updatedAt = new Date();

}

As you can see some of the user fields such as name or email have @Column(unique = true) or @Column(nullable = false)

When I run the application everything is okay and works perfectly.

But when I try to test these two annotations by Junit5 nothing will happen and no exception will be thrown and tests will be passed succesfully.

@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
public class UserRepositoryTests {

    @Autowired
    private UserRepository userRepository;

    @Test
    void ifEmailIsNullMustThrowException() {
        // Arrange
        User user = User.builder()//
                .name("John")//
                .lastName("Doe")//
                .password("password")//
                .phoneNumber("123456789")//
                .build();

        // Act
        User savedUser = userRepository.save(user);

        // Assert
        assertNull(savedUser);
    }

    @Test
    void ifAUserExistWithGivenEmailMustThrowException() {
        // Arrange
        User user1 = User.builder()//
                .name("John")//
                .lastName("Doe")//
                .email("[email protected]")//
                .password("password")//
                .phoneNumber("123456789")//
                .build();
        User user2 = User.builder()//
                .name("John")//
                .lastName("Doe")//
                .email("[email protected]")//
                .password("password")//
                .phoneNumber("123456789")//
                .build();
        userRepository.save(user1);

        // Act
        User savedUser = userRepository.save(user2);

        // Assert
        assertNull(savedUser);
    }

}

Solution

  • The @DataJpaTest makes your test @Transactional which means everything in the test runs in a transaction and will rollback after the test. That combined with the default flush mode of AUTO (which will flush automatically on commit) makes it that no actual SQL will be issues to the database in your test. (Enabling query logging would show this). With no SQL to the database no constraints will trigger and thus no exception.

    To fix you can do 2 things.

    1. Use saveAndFlush instead of save, as that will flush changes to the database and thus issue SQL.
    2. Inject the TestEntityManager and after all your calls, before the asserts, call the flush method so synchronize the changes to the database and issue the SQL.

    Either will work as it will trigger the flushing of the changes.