Dernière modification : 07/02/2021

Spring Boot - Tests

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.

1. Introduction

Nous utiliserons une entitée Book, un repository BookRepository un service BookService et enfin un controller BookController dans ce ticket :

1.1 Configuration du pom.xml


	org.springframework.boot
	spring-boot-starter-web



	org.springframework.boot
	spring-boot-starter-test
	test



	com.h2database
	h2
	runtime



	org.hibernate
	hibernate-entitymanager



	javax.validation
	validation-api



	org.springframework.boot
	spring-boot-starter-data-jpa

1.2 Configuration de l'entité Book

@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;
    }
}

2. Test du repository

Le repository à tester est le suivant :

@Repository
public interface BookRepository extends JpaRepository {
    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.

4 Test du service

Le service à tester est le suivant :

@Service
public class BookService {
    @Autowired
    private BookRepository bookRepository;

    public List 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.

5. Configuration du controller

Le controller à tester est le suivant :

@RestController
@RequestMapping("/api")
public class BookController {
    @Autowired
    private BookService bookService;

    @GetMapping(value = "/books")
    public List 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 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.

6. Conclusion

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.