Last active
May 9, 2024 14:41
-
-
Save rponte/c45456e58c323b62a8180485f3771b46 to your computer and use it in GitHub Desktop.
AssertJ: Example of a jUnit5 test for asserting Bean Validation errors thrown by Spring Boot Repository
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import org.junit.jupiter.api.BeforeEach; | |
import org.junit.jupiter.api.DisplayName; | |
import org.junit.jupiter.api.Test; | |
import org.springframework.beans.factory.annotation.Autowired; | |
import org.springframework.boot.test.context.SpringBootTest; | |
import org.springframework.dao.DataIntegrityViolationException; | |
import org.springframework.test.context.ActiveProfiles; | |
import org.springframework.transaction.TransactionSystemException; | |
import javax.validation.ConstraintViolation; | |
import javax.validation.ConstraintViolationException; | |
import java.util.Optional; | |
import static org.assertj.core.api.Assertions.*; | |
import static org.assertj.core.api.InstanceOfAssertFactories.iterable; | |
import static org.assertj.core.groups.Tuple.tuple; | |
import static org.junit.jupiter.api.Assertions.*; | |
@SpringBootTest | |
@ActiveProfiles("test") | |
class BookRepositoryTest { | |
@Autowired | |
private BookRepository repository; | |
@BeforeEach | |
void setUp() { | |
repository.deleteAll(); | |
} | |
@Test | |
@DisplayName("must save a book") | |
void t1() { | |
// scenario | |
Book book = new Book("9788550800653", "Domain Drive Design", "DDD"); | |
// action | |
repository.save(book); | |
// validation | |
assertEquals(1, repository.findAll().size()); | |
} | |
@Test | |
@DisplayName("should not save a book with invalid parameters") | |
void t2() { | |
// scenario | |
Book book = new Book("97885-invalid", "a".repeat(121), ""); | |
// action and validation | |
// alternative #1 | |
Throwable springException = catchException(() -> repository.save(book)); // or assertThrows() from jUnit | |
assertThat(springException) | |
.isInstanceOf(TransactionSystemException.class) | |
.hasRootCauseInstanceOf(ConstraintViolationException.class) | |
.getRootCause() | |
.extracting("constraintViolations", as(iterable(ConstraintViolation.class))) | |
.extracting( | |
t -> t.getPropertyPath().toString(), | |
ConstraintViolation::getMessage | |
) | |
.containsExactlyInAnyOrder( | |
tuple("isbn", "invalid ISBN"), | |
tuple("title", "size must be between 0 and 120"), | |
tuple("description", "must not be blank") | |
) | |
; | |
// alternative #2 (my favorite one!) | |
assertThatThrownBy(() -> { repository.save(book); }) | |
.isInstanceOf(TransactionSystemException.class) | |
.hasRootCauseInstanceOf(ConstraintViolationException.class) | |
.getRootCause() | |
.extracting("constraintViolations", as(iterable(ConstraintViolation.class))) | |
.extracting( | |
t -> t.getPropertyPath().toString(), | |
ConstraintViolation::getMessage | |
) | |
.containsExactlyInAnyOrder( | |
tuple("isbn", "invalid ISBN"), | |
tuple("title", "size must be between 0 and 120"), | |
tuple("description", "must not be blank") | |
) | |
; | |
// alternative #3 | |
assertThatCode(() -> { repository.save(book); }) | |
.isInstanceOf(TransactionSystemException.class) | |
.hasRootCauseInstanceOf(ConstraintViolationException.class) | |
.getRootCause() | |
.extracting("constraintViolations", as(iterable(ConstraintViolation.class))) | |
.extracting( | |
t -> t.getPropertyPath().toString(), | |
ConstraintViolation::getMessage | |
) | |
.containsExactlyInAnyOrder( | |
tuple("isbn", "invalid ISBN"), | |
tuple("title", "size must be between 0 and 120"), | |
tuple("description", "must not be blank") | |
) | |
; | |
// alternative #4 | |
assertThatExceptionOfType(TransactionSystemException.class) | |
.isThrownBy(() -> repository.save(book)) | |
.withRootCauseInstanceOf(ConstraintViolationException.class) | |
.havingRootCause() | |
.extracting("constraintViolations", as(iterable(ConstraintViolation.class))) | |
.extracting( | |
t -> t.getPropertyPath().toString(), | |
ConstraintViolation::getMessage | |
) | |
.containsExactlyInAnyOrder( | |
tuple("isbn", "invalid ISBN"), | |
tuple("title", "size must be between 0 and 120"), | |
tuple("description", "must not be blank") | |
) | |
; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import javax.persistence.*; | |
import javax.validation.constraints.NotBlank; | |
import javax.validation.constraints.Size; | |
import org.hibernate.validator.constraints.ISBN; | |
import java.util.Objects; | |
import static org.hibernate.validator.constraints.ISBN.Type.ISBN_13; | |
@Entity | |
@Table( | |
uniqueConstraints = { | |
@UniqueConstraint(name = "uk_isbn", columnNames = "isbn") | |
} | |
) | |
public class Book { | |
@Id | |
@GeneratedValue | |
private Long id; | |
@NotBlank | |
@ISBN(type = ISBN_13) | |
@Column(nullable = false, length = 13) | |
private String isbn; | |
@NotBlank | |
@Size(max = 120) | |
@Column(nullable = false, length = 120) | |
private String title; | |
@NotBlank | |
@Size(max = 4000) | |
@Column(nullable = false, length = 4000) | |
private String description; | |
/** | |
* @deprecated Exclusive use of Hibernate | |
*/ | |
@Deprecated | |
public Book() {} | |
public Book(String isbn, String title, String description) { | |
this.isbn = isbn; | |
this.title = title; | |
this.description = description; | |
} | |
public Long getId() { | |
return id; | |
} | |
@Override | |
public boolean equals(Object o) { | |
if (this == o) return true; | |
if (o == null || getClass() != o.getClass()) return false; | |
Book book = (Book) o; | |
return Objects.equals(isbn, book.isbn); | |
} | |
@Override | |
public int hashCode() { | |
return Objects.hash(isbn); | |
} | |
@Override | |
public String toString() { | |
return "Book{" + | |
"id=" + id + | |
", isbn='" + isbn + '\'' + | |
", title='" + title + '\'' + | |
", description='" + description + '\'' + | |
'}'; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import org.springframework.data.jpa.repository.JpaRepository; | |
import org.springframework.stereotype.Repository; | |
import org.springframework.transaction.annotation.Transactional; | |
@Repository | |
@Transactional(readOnly = true) | |
public interface BookRepository extends JpaRepository<Book, Long> { | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thank you very much <3