使用 MockMvc 和 AutoConfigureMockMvc 测试 Spring Boot Web 应用程序时 LazyInitializationException 的原因是什么

问题描述

我正在尝试本教程中的一些 Spring Boot MVC 单元测试示例 - https://spring.io/guides/gs/testing-web/。我的项目有 Spring Boot MVC 和 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 Boot 应用程序启动时工作。食谱列表显示在浏览器中。

这个带有 SpringBoottest 和 TestRestTemplate 的 test case 按预期工作。

@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)

这看起来像是事务范围的问题。任何人都可以帮助找到根本原因吗?与在 MVC 层(@SpringBoottest 和 @AutoConfiguremockmvc)测试相比,作为一个完整的 Web 应用程序(使用 @SpringBoottest)进行测试时,需要哪些额外的配置来测试 Spring 应用程序上下文?

解决方法

解决方案是在调用 Mock MVC 的 Test 方法中添加@Transactional。

@Test
@Transactional
public void testIndexControllerView() throws Exception {
    this.mockMvc.perform(MockMvcRequestBuilders.get("/")).andExpect(status().isOk());
}

此答案涉及对 JPA 存储库对象进行单元测试的类似问题。

LazyInitializationException: could not initialize proxy - no Session

我还不确定在没有 HTTP 服务器的情况下测试控制器时为什么需要事务范围。