Search code examples
javaspringspring-bootspring-restcontrollerspring-boot-test

How can I run a REST Controller test without loading data.sql?


I'm developing a Spring Boot application and I'm having some trouble setting up RESTController tests. The problem is that, when I run the test classes individually, they all work. However, when I try to run all the test classes at once, only the first one works and the others throw java.lang.IllegalStateException: Failed to load ApplicationContext. I have been debugging this and the problematic line is the following:

INSERT INTO users(username,password, email) VALUES ('admin','$2a$10$bicbzJTFskk8.sHWJauxCu2RzDIqXk/zCxQDZ5ByLQw0m0lQ6l2Pa', 'admin@mail.com') [23505-200] Caused by: org.springframework.jdbc.datasource.init.ScriptStatementFailedException: Failed to execute SQL script statement #1 of URL [file:spring-boot/target/classes/db/hsqldb/data.sql]: INSERT INTO users(username,password, email) VALUES ('admin','$2a$10$bicbzJTFskk8.sHWJauxCu2RzDIqXk/zCxQDZ5ByLQw0m0lQ6l2Pa', 'admin@mail.com'); nested exception is org.h2.jdbc.JdbcSQLIntegrityConstraintViolationException: Unique index or primary key violation: "PUBLIC.PRIMARY_KEY_4D ON PUBLIC.USERS(USERNAME) VALUES 1"; SQL statement:

This leads me to think that the data.sql script is being executed on every test (which I believe should not be the case since my controller tests shouldn't rely on DB data). On top of that, the data is not being flushed after executing each class, so the first one works fine and the rest throw a @Unique exception because the data is already there.

My REST controller tests look like this:

@DirtiesContext(classMode = ClassMode.BEFORE_CLASS)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc(addFilters = false)
public class GameControllerTest {

    @Autowired
    MockMvc mockMvc;

    @MockBean
    GameService gameService;

    @BeforeEach
    void setup() {
        Game game1 = new Game();
        game1.setId(1);

        Game game2 = new Game();
        game2.setId(2);

        Lobby lobby = new Lobby();

        when(gameService.findAll()).thenReturn(List.of(game1, game2));
        when(gameService.createFromLobby(lobby)).thenReturn(game1);
        when(gameService.gameCount()).thenReturn(List.of(game1, game2).size());
        when(gameService.findGameById(1)).thenReturn(game1);

    }

    @Test
    void testGetAllGames() throws Exception {
        mockMvc.perform(get("/games")).andExpect(status().isOk()).andExpect(jsonPath("$", hasSize(2)))
                .andExpect(jsonPath("$[0].id", is(1)))
                .andExpect(jsonPath("$[1].id", is(2)));
    }

As you can see, I also tried to use @DirtiesContext(classMode = ClassMode.BEFORE_CLASS) but it does not fix the problem.


Solution

  • Thanks both for your answers. I had to do the following changes:

    @ExtendWith(SpringExtension.class)
    @WebMvcTest(controllers = GameController.class)
    public class GameControllerTest {
    
        @Autowired
        MockMvc mockMvc;
    
        @MockBean
        private GameService gameService;
    
        // I also have to mock all the services called from gameService:
    
        @MockBean
        private UserService userService;
    
        @MockBean
        private PlayerService playerService;
    
        @MockBean
        private DataSource dataSource; 
    
        // (...)
    

    I didn't know that I also had to mock the services that were not called directly by the service. This way, @WebMvcTest works as expected.