Dans cet article nous allons voir comment tester une application Spring Boot. Dans un premier temps nous allons utiliser les annotations @DataJpaTest, puis @MockBean et enfin @WebMvcTest.
Nous utiliserons une entitée Book, un repository BookRepository un service BookService et enfin un controller BookController dans ce ticket :
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
@Entity
@Table(name = "book")
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Size(min = 1, max = 30)
@NotNull
private String title;
public Long getId() {
return this.id;
}
public String getTitle() {
return this.title;
}
public void setId(Long id) {
this.id = id;
}
public void setTitle(String title) {
this.title = title;
}
}
Le repository à tester est le suivant :
@Repository
public interface BookRepository extends JpaRepository<Book, Long> {
public Book findByTitle(String title);
}
L'annotation @DataJpaTest est utilisée pour tester les référentiels JPA. L'annotation applique la configuration pertinente pour les tests JPA. Par défaut, les tests annotés avec @DataJpaTest utilisent une base de données H2 en mémoire.
Classe de test du repository :
@ExtendWith(SpringExtension.class)
@DataJpaTest
class BookRepositoryTest {
private static final String BOOK_TITLE = "The Little Prince";
@Autowired
private TestEntityManager entityManager;
@Autowired
private BookRepository bookRepository;
@Test
void whenFindByTitle_thenReturnBook() {
// GIVEN
final Book book = new Book();
book.setTitle(BOOK_TITLE);
this.entityManager.persist(book);
this.entityManager.flush();
// WHEN
final Book bookBDD = this.bookRepository.findByTitle(BOOK_TITLE);
// THEN
assertEquals(BOOK_TITLE, bookBDD.getTitle());
}
}
Dans le test ci-dessus, nous créons un livre dans la base de données, puis nous le recherchons via l'API. Enfin nous vérifions que le livre récupéré correspond bien à ce qui est attendu.
Le service à tester est le suivant :
@Service
public class BookService {
@Autowired
private BookRepository bookRepository;
public List<Book> getAllBooks() {
return this.bookRepository.findAll();
}
public Book getBookByTitle(String title) {
return this.bookRepository.findByTitle(title);
}
}
Classe de test du service :
@ExtendWith(SpringExtension.class)
class BookServiceTest {
@TestConfiguration
static class BookServiceTestContextConfiguration {
@Bean
public BookService employeeService() {
return new BookService();
}
}
private static final String BOOK_TITLE = "The Little Prince";
@Autowired
private BookService employeeService;
@MockBean
private BookRepository bookRepository;
@BeforeEach
public void setUp() {
final Book book = new Book();
book.setTitle(BOOK_TITLE);
Mockito.when(this.bookRepository.findByTitle(book.getTitle()))
.thenReturn(book);
}
@Test
void whenValidName_thenEmployeeShouldBeFound() {
// GIVEN
final String title = BOOK_TITLE;
// WHEN
final Book bookBDD = this.employeeService.getBookByTitle(title);
// THEN
assertEquals(title, bookBDD.getTitle());
}
}
On notera l'utilisation de l'annotation @TestConfiguration afin d'obtenir une instance de la classe du service en tant que @Bean afin qu'elle soit disponible via l'annotation @Autowire. Cela permet d'injecter la dépendance du @MockBean dans le service automatiquement. De plus, sans l'annotation @TestConfiguration, nous aurions une erreur, car lors de l'analyse, des composants et des configurations créés uniquement pour des tests sont accidentellement récupérés. Par conséquent, cette annotation indique qu'elles ne doivent pas être détectées par l'analyse.
Le controller à tester est le suivant :
@RestController
@RequestMapping("/api")
public class BookController {
@Autowired
private BookService bookService;
@GetMapping(value = "/books")
public List<Book> getAllBooks() {
return this.bookService.getAllBooks();
}
}
Classe de test du controller :
@ExtendWith(SpringExtension.class)
@WebMvcTest(BookController.class)
class BookControllerTest {
private static final String BOOK_TITLE = "The Little Prince";
@Autowired
private MockMvc mvc;
@MockBean
private BookService service;
@Test
void givenBooks_whenGetBooks_thenReturnJsonArray() throws Exception {
// GIVEN
final Book book = new Book();
book.setTitle(BOOK_TITLE);
final List<Book> books = Arrays.asList(book);
// WHEN
Mockito.when(this.service.getAllBooks()).thenReturn(books);
this.mvc.perform(get("/api/books")
// THEN
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$[0].title", is(book.getTitle())));
}
}
Ici nous utilisons @WebMvcTest qui configure automatiquement l'infrastructure Spring MVC pour nos tests unitaires. Couplé avec @MockBean, nous simulons un livre lors de l'appel au service par le controller. Enfin, la méthode get est utilisée pour réaliser l'appel au controller.
Nous avons vu comment créer des tests unitaires dans l'ensemble des couches d'une application standard : le repository, le service et le controller. Il serait intéressant désormais de voir comment créer des tests d'intégrations.
LauLem.com - Conditions Générales d'Utilisation - Informations Légales - Charte relative aux cookies - Charte sur la protection des données personnelles - A propos