spring - 在木星 @BeforeAll 方法中得到 LazyInitializationException
问题描述
在我的集成测试中,我想使用@BeforeAll(见下文)预先在我的系统中填充一些我可以在我的测试用例中依赖的数据。
package com.ksteindl.chemstore.service;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import javax.transaction.Transactional;
//some other import of my own stuff
@ExtendWith(SpringExtension.class)
@ActiveProfiles("test")
@SpringBootTest(webEnvironment= SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
public class ShelfLifeServiceTest extends BaseControllerTest {
@Autowired
private ShelfLifeService shelfLifeService;
@Autowired
private ChemTypeService chemTypeService;
@BeforeAll
static void setUpTestDb(@Autowired ShelfLifeService shelfLifeService,@Autowired ChemTypeService chemTypeService) {
ShelfLifeInput input = LabAdminTestUtils.getSolidForAlphaInput();
Long chemTypeId = chemTypeService.getChemTypes().stream()
.filter(chemType -> chemType.getName().equals(LabAdminTestUtils.SOLID_COMPOUND_NAME))
.findAny()
.get()
.getId();
input.setChemTypeId(chemTypeId);
shelfLifeService.createShelfLife(input, AccountManagerTestUtils.BETA_LAB_MANAGER_PRINCIPAL);
}
@Test
@Rollback
@Transactional
public void testCreateShelfLife_whenAllValid_gotNoException() {
ShelfLifeInput input = LabAdminTestUtils.getSolidForBetaInput();
Long chemTypeId = chemTypeService.getChemTypes().stream()
.filter(chemType -> chemType.getName().equals(LabAdminTestUtils.SOLID_COMPOUND_NAME))
.findAny()
.get()
.getId();
input.setChemTypeId(chemTypeId);
shelfLifeService.createShelfLife(input, AccountManagerTestUtils.BETA_LAB_MANAGER_PRINCIPAL);
}
}
我的问题是当我运行测试时,我从 @BeforeAll 方法抛出了一个 LazyIncializtionException
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.ksteindl.chemstore.domain.entities.Lab.labManagers, could not initialize proxy - no Session
...
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.base/java.util.stream.ReferencePipeline.anyMatch(ReferencePipeline.java:528)
at com.ksteindl.chemstore.service.ShelfLifeService.getAndValidateLab(ShelfLifeService.java:92)
at com.ksteindl.chemstore.service.ShelfLifeService.createOrUpdateShelfLife(ShelfLifeService.java:65)
at com.ksteindl.chemstore.service.ShelfLifeService.createShelfLife(ShelfLifeService.java:47)
at com.ksteindl.chemstore.service.ShelfLifeServiceTest.setUpTestDb(ShelfLifeServiceTest.java:39)
有趣的是,当我注释掉 @BeforeAll 方法时,我的测试运行正常,没有任何异常。可以看到,两种方法的内容几乎是一样的(只是输入不同,防止相互冲突)。抛出异常的确切代码是这样的:
private Lab getAndValidateLab(String labKey, Principal principal) {
Lab lab = labService.findLabByKey(labKey);
lab.getLabManagers().stream().filter(/*some predicate*/).findAny().orElseThrow(/*throw validation Exception*/);
return lab;
}
没错,Lab 和 labManagers 属性之间的关系是惰性的,但我不明白为什么 Spring 不会在一个事务中获取这些数据,就像在testCreateShelfLife_whenAllValid_gotNoException
. 另外,ShelfLifeService.createShelfLife()
从 Rest Controller 调用中当然可以正常工作。谢谢你的帮助!实体:
@Entity
@Data
public class Lab {
//...
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "MANAGER_OF_LAB_TABLE", joinColumns = @JoinColumn(name = "LAB_ID"), inverseJoinColumns = @JoinColumn(name = "APP_USER_ID"))
@JsonIgnore
private List<AppUser> labManagers;
}
@Entity
@Data
public class AppUser {
//...
@ManyToMany(mappedBy = "labManagers")
private List<Lab> managedLabs;
}
解决方案
将 @BeforeAll(在 Spring 容器初始化之前运行)更改为@BeforeEach(在每次测试之前,在 Spring 容器初始化之后运行),但在测试和 Spring 容器构建之后。
我认为 chemTypeService 是在 Spring 容器完全构建之前在 @BeforeAll 中执行的。- 一点猜测
还要查看@DirtiesContext以在每次测试之前重新构建 Spring 上下文。
看这个教程
另一个有点hacky的解决方案是在spring容器初始化后创建自己的类来加载测试数据:
@Service
@Profile("test")
public class TestInit {
@Autowired
private ShelfLifeService shelfLifeService;
@Autowired
private ChemTypeService chemTypeService;
@PostConstruct
private void init() {
ShelfLifeInput input = LabAdminTestUtils.getSolidForAlphaInput();
Long chemTypeId = chemTypeService.getChemTypes().stream()
.filter(chemType -> chemType.getName().equals(LabAdminTestUtils.SOLID_COMPOUND_NAME))
.findAny()
.get()
.getId();
input.setChemTypeId(chemTypeId);
shelfLifeService.createShelfLife(input, AccountManagerTestUtils.BETA_LAB_MANAGER_PRINCIPAL);
}
}
推荐阅读
- angular - 仅尝试更改 agm 方向航点标记的图标
- python - 如何获得两个相邻字母被切换/交换的所有字符串变体
- amazon-web-services - lambda - 用户无权执行:cognito-idp:ListUsers
- postgresql - PostgreSQL。修改表的函数的独占权限
- kubernetes - Kubernetes 仪表板服务器上的错误(“未知”)导致请求无法成功
- javascript - 禁用和启用注释字段取决于注释字段值是否为空
- laravel - 在 laravel 中选择最低价格与关系
- c++ - 使用二进制数据点生成 XML VTK 文件
- python - 尝试用 Python 编写高质量的 selenium 脚本
- docker - 如何在生产中使用 docker-compose 中的环境变量?