前言: 我在单元测试中遇到了 LazyInitializationException 的问题,而且我很难理解它,正如你从我的问题中看到的那样Spring 中的数据库会话, TestNG 和 Spring 3使用 TestNG 对在 Spring 中使用的 Hibernate 实体类进行单元测试时出现 LazyInitializationException

为了能够清楚地提出我的问题,我在GitHub上做了一个示例项目: http://github.com/niklassaers/Sample-Spring3-App/ 在此示例项目中,我重现了我在 Spring3/Hibernate3/TestNG 项目中遇到的问题。

问题: 我有两个单元测试,它们非常相似,使用相同的服务测试相同的项目集合的相同类。一位成功,一位失败。失败者为何会失败?(或者,为什么正在运行的程序不会以同样的方式失败?)

这是失败的测试:

@Test(timeOut=1000)
public void Roles() {
    User mockUser = userService.read(1);
    Assert.assertNotNull(mockUser);
    Assert.assertTrue(mockUser.isValid());
    Set<Role> roles = mockUser.getRoles();
    int size = roles.size();  // This line gives a LazyInitializationException
    Assert.assertTrue(size > 0);
}

完整代码位于 ( http://github.com/niklassaers/Sample-Spring3-App/blob/master/src/tld/mydomain/sample/entities/test/FailingUserUnitTest.java )

这是运行测试:

@Test
public void Roles() {
    for(int i = 1; i <= 4; i++) {
        User user = userService.read(i);
        Assert.assertNotNull(user);
        Assert.assertTrue(user.isValid());
        Set<Role> roles = user.getRoles();
        Assert.assertTrue(roles.size() > 0); // This line does not give a LazyInitializationException
        for(Role r : roles) {
            Assert.assertNotNull(r.getName());
            for(User someUser : r.getUsers())
                Assert.assertNotNull(someUser.getName());
        }
    }
}

完整代码位于 ( http://github.com/niklassaers/Sample-Spring3-App/blob/master/src/tld/mydomain/sample/entities/test/UserUnitTest.java )

下面是运行我的测试的控制台输出。我知道我将服务包装在 TransactionProxyFactoryBean 中(请参阅 http://github.com/niklassaers/Sample-Spring3-App/blob/master/WebRoot/WEB-INF/App-Model.xml),这使得它们在事务中运行,并且单元测试没有被包装,使测试像一个视图。我用 OpenSessionInViewInterceptor “修复”的视图。但我了解到,在从 AbstractTransactionalTestNGSpringContextTests 扩展的类中用 @Test 注释的每个单元测试也应该包装在自己的事务中,实际上,我已经注释了这两个类以在每个测试完成后回滚事务。这就是为什么我对为什么一个测试失败而另一个测试没有失败感到双重困惑。有什么线索或解决方案吗?

您可以根据需要随意修改 GitHub 上的示例项目,所有代码都应该在那里,但为了简单起见,我省略了 jar 文件。这是承诺的完整输出:

[Parser] Running:
  /Users/niklas/Documents/Eclipse/SampleProject/testng.xml

2009-10-15 10:16:16,066 [TestNGInvoker-Roles()] ERROR org.hibernate.LazyInitializationException - failed to lazily initialize a collection of role: tld.mydomain.sample.entities.User.roles, no session or session was closed
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: tld.mydomain.sample.entities.User.roles, no session or session was closed
    at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:380)
    at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:372)
    at org.hibernate.collection.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:119)
    at org.hibernate.collection.PersistentSet.size(PersistentSet.java:162)
    at tld.mydomain.sample.entities.test.FailingUserUnitTest.Roles(FailingUserUnitTest.java:33)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.testng.internal.MethodHelper.invokeMethod(MethodHelper.java:607)
    at org.testng.internal.InvokeMethodRunnable.runOne(InvokeMethodRunnable.java:49)
    at org.testng.internal.InvokeMethodRunnable.run(InvokeMethodRunnable.java:40)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:441)
    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
    at java.util.concurrent.FutureTask.run(FutureTask.java:138)
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
    at java.lang.Thread.run(Thread.java:637)

===============================================
SampleAppSuite
Total tests run: 3, Failures: 1, Skips: 0
===============================================

干杯

尼克

有帮助吗?

解决方案

从 @Test 注释中删除 timeOut=1000。这似乎导致测试在单独的线程中运行(从堆栈跟踪可以明显看出,其中异常是从线程池抛出的)。事务和 SessionFactory 绑定到主线程,而不是绑定到测试运行程序的线程,这导致了此异常。

我已经运行了您的示例代码并已使测试正常工作。将来,如果您在依赖项中包含 Maven2 pom.xml 将会很方便,这样那些尝试编译代码的人就可以更轻松地实际执行此操作。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top