Testing Spring Boot Applications

Spring Boot Test

Spring Boot Test comes with a wide range of tools for testing all or part of an application. As you will see when consulting the documentation, there are many possible combinations. In general, you should try to get an idea of the test you want to write, then refer to the documentation to determine the appropriate combination of annotations.

Below, we will describe two nominal scenarios: an integration test with the database, followed by testing an API endpoint with the mocked database.

Testing Repositories With the Database

In this scenario, we will use the @DataJpaTest annotation. It triggers the following behaviors:

  • The configured database is replaced by an in-memory H2 database.

  • Only beans related to the data access layer are loaded into the context.

  • A transaction rollback implicitly occurs after each test method is executed, so that the database remains in the same state from one test to the next.

Note
Since we decided not to create the database when the application starts up, the tests crash. To resolve the issue, the spring.jpa.hibernate.ddl-auto property is overridden.
 1import org.junit.jupiter.api.Test;
 2import org.springframework.beans.factory.annotation.Autowired;
 3import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
 4
 5import java.util.List;
 6
 7import static org.junit.jupiter.api.Assertions.*;
 8
 9@DataJpaTest(properties = {"spring.jpa.hibernate.ddl-auto=create-drop"})
10class ProductRepositoryTest {
11
12    @Autowired
13    private ProductRepository productRepository;
14
15    @Test
16    void shouldSaveAndReloadProduct() {
17        Product p1 = new Product();
18        p1.setName("Product 1");
19        p1.setPrice(10.0);
20        productRepository.save(p1);
21
22        List<Product> products = productRepository.findAll();
23        assertEquals(products.size(), 1);
24        assertNotNull(products.getFirst());
25        assertTrue(products.getFirst().getId() > 0);
26        assertEquals(products.getFirst().getName(), "Product 1");
27        assertEquals(products.getFirst().getPrice(), 10.0);
28    }
29
30}

In this scenario, we use the @Sql annotation to initialize data in the database using a SQL script before running the test.

 1import org.junit.jupiter.api.Test;
 2import org.springframework.beans.factory.annotation.Autowired;
 3import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
 4import org.springframework.test.context.jdbc.Sql;
 5
 6import java.util.List;
 7
 8import static org.assertj.core.api.Assertions.*;
 9import static org.springframework.test.context.jdbc.Sql.ExecutionPhase.*;
10
11@DataJpaTest(properties = {"spring.jpa.hibernate.ddl-auto=create-drop"})
12class OrderRepositoryTest {
13
14    @Autowired
15    private OrderRepository orderRepository;
16
17    @Test
18    @Sql(scripts = {"/test-data.sql"}, executionPhase = BEFORE_TEST_METHOD)
19    void shouldFind2OrdersWithOrderLineQuantityGreaterThan2() {
20        List<Order> orders = orderRepository.findByOrderLinesQuantityGreaterThanEqual(2);
21        assertThat(orders).hasSize(2);
22    }
23
24}
1DELETE FROM order_lines;
2DELETE FROM orders;
3DELETE FROM products;
4
5COMMIT;
6
7INSERT INTO products (id, name, price) VALUES (1, 'Widget A', 19.99), (2, 'Widget B', 29.99), (3, 'Widget C', 109.99);
8INSERT INTO orders (id, customer) VALUES (1, 'Alice'), (2, 'Bob'), (3, 'Clara');
9INSERT INTO order_lines (id, quantity, product_id, order_id) VALUES (1, 1, 1, 1),  (2, 1, 2, 1), (3, 10, 1, 2), (4, 1, 1, 3), (5, 10, 2, 3), (6, 1, 3, 3)

Testing an API Endpoint

TODO