2

I'm Using Junit in Spring Boot, along with TestContainers (Docker, MySQL 8.0.29) to develop integration tests.

When I execute my tests individually, they all succeed. However when I run them all at once (i.e. in CI/CD), they fail. This is because the tests are not executed in order, and an item might already be deleted before the test to find the item is executed.

To fix this I want to give the entities a unique ID. However, the ID is already automaticly set in my Hibernate entity:

@Entity
public class Assignment {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

I've tried to delete all items before each test is executed, however this does not work:

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @BeforeEach
    void tearDown() {
        JdbcTestUtils.deleteFromTables(jdbcTemplate, "assignment");
    }

Example integration test:

    @Test
    void When_getById_Verify_Fields() {
        AssignmentDTO assignmentDTO = new AssignmentDTO();
        assignmentDTO.setTitle("test");
        assignmentDTO.setDescription("test");
        assignmentDTO.setUserId("1");
        assignmentDTO.setCreator("1");

        assignmentService.addAssignment(assignmentDTO);

        AssignmentDTO expectedAssignment = assignmentService.getById(1);

        assertEquals(assignmentDTO.getTitle(), expectedAssignment.getTitle());
        assertEquals(assignmentDTO.getDescription(), expectedAssignment.getDescription());
        assertEquals(assignmentDTO.getUserId(), expectedAssignment.getUserId());
        assertEquals(assignmentDTO.getCreator(), expectedAssignment.getCreator());
    }
Vercors
  • 123
  • 2
  • 7

3 Answers3

2

Each test method should generally remove the data it creates to prevent this problem.

What you can do that the data is not committed to the database you can add the @Transactional annotation to the test method or the test class if you want it for all test methods.. This will do a rollback after the test method.

Simon Martinelli
  • 34,053
  • 5
  • 48
  • 82
  • 1
    Simon is very right here. Just solve the issue of test pollution as you would when running against a regular database. While you can solve test pollution on the infrastructure level (container per test), this is not recommended, since it will slow down the suite considerably. Solve test pollution on the logical level instead. – Kevin Wittek Nov 21 '22 at 10:26
2

If you want to execute SQL script before or after each test, you can use annotations from org.springframework.test.context.jdbc.

And an example of how to use it in a test :

@SqlGroup({
        @Sql(executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD, scripts = {
                "classpath:datasets/integration/integration_test_before.sql"}),
        @Sql(executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD, scripts = {
                "classpath:datasets/integration/integration_test_after.sql"})})

These annotations need to be added to the class test. With this annotation, you will execute a script before the test and after to initialize data and delete data.

The SQL files need to be in src/test/resources/datasets/integration.

You can find the link to a guide to use it: https://www.baeldung.com/spring-boot-data-sql-and-schema-sql

Andrzej Sydor
  • 1,373
  • 4
  • 13
  • 28
0

You can easily fix this by returning the object saved at addAssignment or the id. See my suggestion below

AssignmentDTO assignment = assignmentService.addAssignment(assignmentDTO);

AssignmentDTO expectedAssignment = assignmentService.getById(assignment.getId());

The reason why is failing besides you delete all data is because the auto-increment still persist. See this in order to reset the auto-increment.

Eddú Meléndez
  • 6,107
  • 1
  • 26
  • 35