测试 Spring 使用 MockMvc 和 AutoConfigureMockMvc 启动 Web 应用时出现 LazyInitializationException 的原因是什么
What is the cause of LazyInitializationException when testing Spring Boot web app with MockMvc and AutoConfigureMockMvc
我正在尝试一些单元测试示例 Spring 本教程中的引导 MVC - https://spring.io/guides/gs/testing-web/. My project has Spring Boot MVC and JPA (https://github.com/shankarps/SfgUdemyRecipes)
被测试的控制器检索与类别实体具有多对多映射的食谱实体列表。
@RequestMapping({"", "/", "/index"})
public String getIndexPage(Model model){
log.info("Getting all recipes");
model.addAttribute("recipes", recipeService.getAllRecipes());
return "index";
}
此控制器在 Spring 启动应用程序启动时工作。食谱列表显示在浏览器中。
此 test case 与 SpringBootTest 和 TestRestTemplate 按预期工作。
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class IndexControllerHttpTest {
@LocalServerPort
private int port;
@Autowired
private TestRestTemplate restTemplate;
@Test
public void testIndexUrl(){
String response = this.restTemplate.getForObject("http://localhost:"+port+"/", String.class);
}
}
但是,当我 运行 test case with MockMVC 将控制器作为 MVC 对象(使用 @AutoConfigureMockMvc)进行测试时,没有 Http 服务器,我得到 org.hibernate.LazyInitializationException
异常。
@SpringBootTest
@AutoConfigureMockMvc
public class IndexControllerMockMVCTest {
@Autowired
private MockMvc mockMvc;
@Test
public void testIndexControllerView() throws Exception {
this.mockMvc.perform(MockMvcRequestBuilders.get("/")).andExpect(status().isOk());
}
}
这里是完整的异常堆栈跟踪:
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.shankar.sfg.recipes.recipes.domain.Category.recipes, could not initialize proxy - no Session
at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:606)
at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:218)
at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:585)
at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:149)
at org.hibernate.collection.internal.PersistentSet.toString(PersistentSet.java:327)
at java.lang.String.valueOf(String.java:2994)
这看起来像是事务范围的问题。任何人都可以帮助找到根本原因吗?当作为完整的 Web 应用程序(使用@SpringBootTest)进行测试时,与在 MVC 层进行测试(@SpringBootTest 和@AutoConfigureMockMvc)相比,测试 Spring 应用程序上下文需要哪些额外的配置)?
解决方案是将@Transactional 添加到调用 Mock MVC 的测试方法中。
@Test
@Transactional
public void testIndexControllerView() throws Exception {
this.mockMvc.perform(MockMvcRequestBuilders.get("/")).andExpect(status().isOk());
}
此答案处理对 JPA 存储库对象进行单元测试的类似问题。
我还不确定为什么在没有 HTTP 服务器的情况下测试控制器时需要事务范围。
我正在尝试一些单元测试示例 Spring 本教程中的引导 MVC - https://spring.io/guides/gs/testing-web/. My project has Spring Boot MVC and JPA (https://github.com/shankarps/SfgUdemyRecipes)
被测试的控制器检索与类别实体具有多对多映射的食谱实体列表。
@RequestMapping({"", "/", "/index"})
public String getIndexPage(Model model){
log.info("Getting all recipes");
model.addAttribute("recipes", recipeService.getAllRecipes());
return "index";
}
此控制器在 Spring 启动应用程序启动时工作。食谱列表显示在浏览器中。
此 test case 与 SpringBootTest 和 TestRestTemplate 按预期工作。
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class IndexControllerHttpTest {
@LocalServerPort
private int port;
@Autowired
private TestRestTemplate restTemplate;
@Test
public void testIndexUrl(){
String response = this.restTemplate.getForObject("http://localhost:"+port+"/", String.class);
}
}
但是,当我 运行 test case with MockMVC 将控制器作为 MVC 对象(使用 @AutoConfigureMockMvc)进行测试时,没有 Http 服务器,我得到 org.hibernate.LazyInitializationException
异常。
@SpringBootTest
@AutoConfigureMockMvc
public class IndexControllerMockMVCTest {
@Autowired
private MockMvc mockMvc;
@Test
public void testIndexControllerView() throws Exception {
this.mockMvc.perform(MockMvcRequestBuilders.get("/")).andExpect(status().isOk());
}
}
这里是完整的异常堆栈跟踪:
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.shankar.sfg.recipes.recipes.domain.Category.recipes, could not initialize proxy - no Session
at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:606)
at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:218)
at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:585)
at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:149)
at org.hibernate.collection.internal.PersistentSet.toString(PersistentSet.java:327)
at java.lang.String.valueOf(String.java:2994)
这看起来像是事务范围的问题。任何人都可以帮助找到根本原因吗?当作为完整的 Web 应用程序(使用@SpringBootTest)进行测试时,与在 MVC 层进行测试(@SpringBootTest 和@AutoConfigureMockMvc)相比,测试 Spring 应用程序上下文需要哪些额外的配置)?
解决方案是将@Transactional 添加到调用 Mock MVC 的测试方法中。
@Test
@Transactional
public void testIndexControllerView() throws Exception {
this.mockMvc.perform(MockMvcRequestBuilders.get("/")).andExpect(status().isOk());
}
此答案处理对 JPA 存储库对象进行单元测试的类似问题。
我还不确定为什么在没有 HTTP 服务器的情况下测试控制器时需要事务范围。