如何在不加载 data.sql 的情况下 运行 REST 控制器测试?
How can I run a REST Controller test without loading data.sql?
我正在开发 Spring 引导应用程序,但在设置 RESTController 测试时遇到了一些问题。问题是,当我 运行 单独测试 class 时,它们都有效。但是,当我尝试一次 运行 所有测试 class 时,只有第一个有效,其他的抛出 java.lang.IllegalStateException: Failed to load ApplicationContext
。我一直在调试这个,有问题的行如下:
INSERT INTO users(username,password, email) VALUES ('admin','a$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','a$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:
这让我认为 data.sql
脚本在每次测试时都在执行(我认为不应该是这种情况,因为我的控制器测试不应该依赖于数据库数据)。最重要的是,在执行每个 class 后数据没有被刷新,所以第一个工作正常,其余的抛出 @Unique
异常,因为数据已经存在。
我的 REST 控制器测试如下所示:
@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)));
}
如您所见,我也尝试使用 @DirtiesContext(classMode = ClassMode.BEFORE_CLASS)
但它并没有解决问题。
我觉得这个问题有两个不同的问题。
首先,您的问题是,您的整个 SpringBootApplication 已启动,因为您正在使用 @SpringBootTest
,请改用 @WebMvcTest
。您还“需要”使用
@LocalServerPort
private int port;
实际访问您使用 (webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
添加的端口
你的第二个问题
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.
可以通过使用 @Transactional
注释您的测试轻松修复,但我认为这不是您实际问题的解决方案。
感谢两位的回答。我必须进行以下更改:
@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;
// (...)
我不知道我还必须模拟那些没有被服务直接调用的服务。这样,@WebMvcTest
会按预期工作。
我正在开发 Spring 引导应用程序,但在设置 RESTController 测试时遇到了一些问题。问题是,当我 运行 单独测试 class 时,它们都有效。但是,当我尝试一次 运行 所有测试 class 时,只有第一个有效,其他的抛出 java.lang.IllegalStateException: Failed to load ApplicationContext
。我一直在调试这个,有问题的行如下:
INSERT INTO users(username,password, email) VALUES ('admin','a$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','a$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:
这让我认为 data.sql
脚本在每次测试时都在执行(我认为不应该是这种情况,因为我的控制器测试不应该依赖于数据库数据)。最重要的是,在执行每个 class 后数据没有被刷新,所以第一个工作正常,其余的抛出 @Unique
异常,因为数据已经存在。
我的 REST 控制器测试如下所示:
@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)));
}
如您所见,我也尝试使用 @DirtiesContext(classMode = ClassMode.BEFORE_CLASS)
但它并没有解决问题。
我觉得这个问题有两个不同的问题。
首先,您的问题是,您的整个 SpringBootApplication 已启动,因为您正在使用 @SpringBootTest
,请改用 @WebMvcTest
。您还“需要”使用
@LocalServerPort
private int port;
实际访问您使用 (webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
你的第二个问题
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.
可以通过使用 @Transactional
注释您的测试轻松修复,但我认为这不是您实际问题的解决方案。
感谢两位的回答。我必须进行以下更改:
@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;
// (...)
我不知道我还必须模拟那些没有被服务直接调用的服务。这样,@WebMvcTest
会按预期工作。