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

Time:01-17

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', '[email protected]') [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', '[email protected]'); 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.

CodePudding user response:

I feel like there are two different questions in this one.

First your problem is, that your entire SpringBootApplication is started because you are using @SpringBootTest use @WebMvcTest instead. You also "need" to use

@LocalServerPort 
private int port;

to actually access the port you add with (webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)

Your second problem

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.

Can easily be fixed by annotating your test with @Transactional tho I don't believe this to be the solution for your actual problem.

CodePudding user response:

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.

  •  Tags:  
  • Related