序言:我在单元测试中一直在挣扎着LazyInitializationException,我很难理解它,正如我在Spring,TestNG和Spring 3中的Database Sessions和LazyInitializationException进行单元测试Hibernate时所看到的那样 使用TestNG在Spring中使用的实体类
为了能够清楚地提出我的问题,我在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。看来这导致测试在单独的线程中运行(如stacktrace所示,该异常是从ThreadPool引发的)。事务和SessionFactory绑定到主线程,而不绑定到测试运行程序的线程,这会导致此异常。
我已经运行了您的示例代码,并且已经进行了测试。将来,如果您将Maven2 pom.xml包含在您的依赖项中会很方便,那么对于那些试图编译您的代码的人来说,这样做起来会更容易。