如何在 Spring Boot 集成测试中获取 JPA 上下文?
How to get JPA context in Springboot integration test?
我是运行一个带jpa的Springboot应用。我正在尝试建立一个基于 Cucumber 的集成测试。在我的测试中,当我尝试访问存储库时,我得到一个“org.hibernate.LazyInitializationException”(带有一条消息 "No Session")。这只发生在我的集成测试中,但不会发生在实际应用程序中。一种解决方法是将 @Transactional 放在执行调用的方法上,但如果我在新线程中执行,这将不起作用。
我的第一个问题是:为什么没有 @Transactional 注释它就不能工作?
我的第二个是:为什么它不能在新线程中使用@Transactional 注释?
这是我的代码的简化版本:
黄瓜测试:
@RunWith(Cucumber.class)
@CucumberOptions(features = "src/test/resources/some-integration-test.feature")
public class IntegrationTests {}
黄瓜步骤:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class IntegrationSteps {
@Autowired
SomeRepo repo;
@When("two updates happen at the same time")
public void twoUpdatesHappenAtTheSameTime() {
ExecutorService executorService = Executors.newFixedThreadPool(2);
executorService.execute(set("Thread 1"));
executorService.execute(set("Thread 2"));
executorService.shutdown();
executorService.awaitTermination(1, TimeUnit.MINUTES);
}
public void set(String someThing) {
Some some = repo.getOne(1234);
repo.setSomeThing(someThing);
repo.save(some);
}
}
和回购:
@Repository
public interface SomeRepo extends JpaRepository<Some, Integer> {}
问题似乎出在 getOne()
上。使用 getOne()
您只能获得对该实体的引用。在尝试访问实体的字段之前,不会执行对数据库的真正调用。
通常,当使用 getOne()
时,这些字段会延迟加载,但出于某种原因(我仍然不清楚),这在 SpringBootTest 中不起作用。
我找到了两个解决此问题的方法:
- 用
@Transactional
注释测试。这样您将有一个上下文来加载实体。缺点是您似乎仍然没有获得实体的最新版本。在我的例子中,我更新了代码中的实体,而在测试代码中,实体中没有更新(即使更新发生在 getOne()
调用之前)。
- 不要使用
getOne()
,而是使用 findById()
。缺点是 findById()
是预先加载的,但由于我们仅将其用于测试,因此不会影响我们应用程序的性能。
我是运行一个带jpa的Springboot应用。我正在尝试建立一个基于 Cucumber 的集成测试。在我的测试中,当我尝试访问存储库时,我得到一个“org.hibernate.LazyInitializationException”(带有一条消息 "No Session")。这只发生在我的集成测试中,但不会发生在实际应用程序中。一种解决方法是将 @Transactional 放在执行调用的方法上,但如果我在新线程中执行,这将不起作用。
我的第一个问题是:为什么没有 @Transactional 注释它就不能工作? 我的第二个是:为什么它不能在新线程中使用@Transactional 注释?
这是我的代码的简化版本:
黄瓜测试:
@RunWith(Cucumber.class)
@CucumberOptions(features = "src/test/resources/some-integration-test.feature")
public class IntegrationTests {}
黄瓜步骤:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class IntegrationSteps {
@Autowired
SomeRepo repo;
@When("two updates happen at the same time")
public void twoUpdatesHappenAtTheSameTime() {
ExecutorService executorService = Executors.newFixedThreadPool(2);
executorService.execute(set("Thread 1"));
executorService.execute(set("Thread 2"));
executorService.shutdown();
executorService.awaitTermination(1, TimeUnit.MINUTES);
}
public void set(String someThing) {
Some some = repo.getOne(1234);
repo.setSomeThing(someThing);
repo.save(some);
}
}
和回购:
@Repository
public interface SomeRepo extends JpaRepository<Some, Integer> {}
问题似乎出在 getOne()
上。使用 getOne()
您只能获得对该实体的引用。在尝试访问实体的字段之前,不会执行对数据库的真正调用。
通常,当使用 getOne()
时,这些字段会延迟加载,但出于某种原因(我仍然不清楚),这在 SpringBootTest 中不起作用。
我找到了两个解决此问题的方法:
- 用
@Transactional
注释测试。这样您将有一个上下文来加载实体。缺点是您似乎仍然没有获得实体的最新版本。在我的例子中,我更新了代码中的实体,而在测试代码中,实体中没有更新(即使更新发生在getOne()
调用之前)。 - 不要使用
getOne()
,而是使用findById()
。缺点是findById()
是预先加载的,但由于我们仅将其用于测试,因此不会影响我们应用程序的性能。