Reputation: 8810
In my Spring configuration, I've asked that the session should remain open in my views:
<bean name="openSessionInViewInterceptor" class="org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor">
<property name="sessionFactory" ref="sessionFactory"/>
<property name="flushMode" value="0" />
</bean>
However, this bean obiously doesn't consider my TestNG unit tests as a view. ;-) That's all right, but is there a similar bean for unit tests so that I avoid the dreaded LazyInitializationException while unit-testing? So far, half of my unit tests die because of it.
My unit test typically looks like this:
@ContextConfiguration({"/applicationContext.xml", "/applicationContext-test.xml"})
public class EntityUnitTest extends AbstractTransactionalTestNGSpringContextTests {
@BeforeClass
protected void setUp() throws Exception {
mockEntity = myEntityService.read(1);
}
/* tests */
@Test
public void LazyOneToManySet() {
Set<SomeEntity> entities = mockEntity.getSomeEntitySet();
Assert.assertTrue(entities.size() > 0); // This generates a LazyInitializationException
}
}
I've tried changing the setUp() to this:
private SessionFactory sessionFactory = null;
@BeforeClass
protected void setUp() throws Exception {
sessionFactory = (SessionFactory) this.applicationContext.getBean("sessionFactory");
Session s = sessionFactory.openSession();
TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(s));
mockEntity = myEntityService.read(1);
}
but I believe this is the wrong way to go about it, and I mess the transaction up for later tests. Is there something like an OpenSessionInTestInterceptor, are there better ways of doing this, or is this the way to do it and in that case what have I misunderstood about it?
Cheers
Nik
Upvotes: 1
Views: 2589
Reputation: 100706
Umm.. not to be a smart-ass here, but that's not what setUp()
was intended for.
The basic idea is to have your tests be self-sufficient and re-entrant meaning you should not depend on database having particular records nor should you permanently alter the database in your test. The process, thus, is to:
setUp()
tearDown()
(1), each (2), and (3) all run in separate transactions - hence the problem you're getting with LazyInitializationException. Move mockEntity = myEntityService.read(1);
from setUp into your actual test(s) and it will go away; use setUp if you need to create some test data, not as direct supplement to your individual test.
Upvotes: 1
Reputation: 8512
I uses JUnit for my tests, so you need to adapt the following example to TestNG. Personnaly I use SpringJUnit4ClassRunner to bind transactions in a base test class:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:/applicationContext-struts.xml")
@TransactionConfiguration(transactionManager = "transactionManager")
@Transactional
public abstract class BaseTests {
And in "@Before", I inject a MockHttpServletRequest in the RequestContextHolder:
@Before
public void prepareTestInstance() throws Exception {
applicationContext.getBeanFactory().registerScope("session", new SessionScope());
applicationContext.getBeanFactory().registerScope("request", new RequestScope());
MockHttpServletRequest request = new MockHttpServletRequest();
ServletRequestAttributes attributes = new ServletRequestAttributes(request);
RequestContextHolder.setRequestAttributes(attributes);
.......
I took the information from the manual
Upvotes: 6