I am getting this bug when I run a test case(Junit) for spring application.
I searched for this problem and I got the information that whenever a lazy initialization occurs and my application tries to get second level data while session is closed(object become detached) then this error occurs, we can't make initialization as EAGER as its performance issue.
My testing class contains :
#RunWith(SpringJUnit4ClassRunner.class)
public class MyTestClass extends AbstractControllerTest {
#Rule
public TestName testMethodName = new TestName();
#Before
public void setUp() throws Exception
{
super.setUp();
}
#After
public void tearDown() throws Exception
{
super.tearDown();
}
#Test
public void myTestMethod ()
{
assertTrue("Response Validating",validate(baseEntity,perform()));
}
}
Is there a way that can I put method assertTrue("Response Validating",validate(baseEntity,perform())); in a transaction can bind with current session or with new session so that my detached object become persistent object and then My application can get second level data also. I searched for this problem and I found a solution on link : http://www.jroller.com/RickHigh/entry/hibernate_spring_simulating_an_opensessioninviewfilter
but this link does not fulfil my requirement as it requires target object on which transaction is to be created.
#Test
#Transactional
public void myTestMethod ()
{
assertTrue("Response Validating",validate(baseEntity,perform()));
}
Annotate myTestMethod with #Transactional (assuming you're using annotation-based configuration).
I got the solution for this problem. I was implementing OpenSessionInViewFilter in my testing code to overcome this problem but was doing silly mistake.
Please take a look on following code :
#Autowired
BeanFactory bf;
#Before
public void setUp() throws Exception
{
sessionFactory = (SessionFactory) bf.getBean("sessionFactory");
Session session = SessionFactoryUtils.getSession(sessionFactory, true);
session.setFlushMode(FlushMode.NEVER);
TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
}
#After
public void tearDown() throws Exception
{
super.tearDown();
SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.unbindResource(sessionFactory);
Session session = sessionHolder.getSession();
SessionFactoryUtils.closeSession(session);
}
Earlier I was not using
session.setFlushMode(FlushMode.NEVER) this was the mistake.
BTW thanks
Related
I have the following:
#Repository
#Transactional
#HibernateProfile
public class PersonaHibernateRepository implements PersonaRepository {
private static final Logger logger = LoggerFactory.getLogger(PersonaHibernateRepository.class.getSimpleName());
private final SessionFactory sessionFactory;
public PersonaHibernateRepository(SessionFactory sessionFactory){
logger.info("{} constructor", PersonaHibernateRepository.class.getSimpleName());
this.sessionFactory = sessionFactory;
}
#Override
public Persona saveOne(Persona persona) {
String generatedIdentifier = (String) sessionFactory.getCurrentSession().save(persona);
logger.info("generatedIdentifier: {}", generatedIdentifier);
return persona;
}
...
Each crud method has sessionFactory.getCurrentSession().
With Mockito the following sentence:
when(sessionFactory.getCurrentSession().save(persona)).thenReturn(persona.getId());
always throws java.lang.NullPointerException. I've confirmed sessionFactory.getCurrentSession() is the point of the problem.
I already have read the following:
Mocking Hibernate Session
Unit test of DAO layer with mockito
Thus the java.lang.NullPointerException was removed.
But I always get now:
org.mockito.exceptions.verification.TooManyActualInvocations:
sessionFactory.getCurrentSession();
Wanted 1 time:
-> at com.manuel.jordan.repository.hibernate.PersonaHibernateRepositoryTest_.saveOneTest(PersonaHibernateRepositoryTest_.java:76)
But was 2 times. Undesired invocation:
-> at com.manuel.jordan.repository.hibernate.PersonaHibernateRepository.saveOne(PersonaHibernateRepository.java:43)
These two times happens due the mock invocation and target invocation.
Currently my configuration is:
private PersonaHibernateRepository personaHibernateRepository;
private SessionFactory sessionFactory;
private Session session;
...
#Before
public void setup(){
sessionFactory = mock(SessionFactory.class);
session = mock(Session.class);
personaHibernateRepository = new PersonaHibernateRepository(sessionFactory);
//Removes NullPointerException - 'A'
when(sessionFactory.getCurrentSession()).thenReturn(session);
}
#Test
public void saveOneTest(){
//java.lang.NullPointerException removed thanks to 'A'
when(sessionFactory.getCurrentSession().save(persona)).thenReturn(persona.getId());
Persona persona_ = personaHibernateRepository.saveOne(persona);
assertThat(persona_, is(persona));
//B
verify(sessionFactory).getCurrentSession().save(persona);
}
Just playing, if I change:
From: verify(sessionFactory).getCurrentSession().save(persona);
To: verify(sessionFactory, times(2)).getCurrentSession().save(persona); (observe times(2))
Again appears the java.lang.NullPointerException thrown now by verify(sessionFactory, times(2)).getCurrentSession().save(persona); (B)
Same exception if in #Before the when(sessionFactory.getCurrentSession()).thenReturn(session) is changed to doReturn(session).when(sessionFactory).getCurrentSession()
What is the correct configuration?
In the setup() method you correctly tell Mockito to return the mocked session instance when sessionFactory.getCurrentSession() is invoked so from them on your assertions should focus on the session instance not the sessionFactory. For example:
#Test
public void saveOneTest(){
// you have already told Mockito to return this session instance when sessionFactory.getCurrentSession() is
// invoked so now your when (and optionally verify) should focus on session rather than on sessionFactory
when(session.save(persona)).thenReturn(persona.getId());
Persona persona_ = personaHibernateRepository.saveOne(persona);
assertThat(persona_, is(persona));
verify(session).save(persona);
}
I'm having troubles setting this to work. Basically I want when a tests finishes running for the database to be in the exact same state as before. This happens when using Spring/Hibernate managed sessions and connections, but not for DBUnit. I've tried lots of things and at this point I was doing something like wrapping the shared datasource in TransactionAwareDataSourceProxy and executing the load dataset manually instead of using #DatabaseSetup.
this.databaseConnection = new DatabaseConnection(dataSource.getConnection());
IDataSet xmlFileDataSet = new FlatXmlDataSetBuilder().build(getClass().getResourceAsStream("/dataset.xml"));
DatabaseOperation.REFRESH.execute(databaseConnection, xmlFileDataSet);
And it simply doesn't work. I've checked the dataSource object and it's a TransactionAwareDataSourceProxy instance so everything should be in place. All the data in the dataset is committed and persisted and all the data added/modified inside the spring managed session is not.
Does anyone have a clue what I might be missing or any did this before and ran into the same troubles?
Current code (tried with and without TransactionAwareDataSourceProxy).
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {
RecipeManagementITConfig.class,
SpringJpaTestConfig.class,
DatabaseITConfig.class
},
initializers = {PleaseWork.class})
#TestExecutionListeners({
DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class,
TransactionalTestExecutionListener.class,
DbUnitTestExecutionListener.class
})
#TransactionConfiguration(defaultRollback = true)
#Transactional
public class DefaultIngredientServiceIT {
#Autowired
protected DataSource dataSource;
#Before
public void init() throws Exception {
System.out.println("> " + dataSource); // org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy#d4ce346
dbTester = new DataSourceDatabaseTester(dataSource);
dbTester.setDataSet(getDataSet());
dbTester.setSetUpOperation(DatabaseOperation.REFRESH);
dbTester.onSetup();
}
#After
public void destroy() throws Exception {
dbTester.onTearDown();
}
private IDataSet getDataSet() throws Exception {
return new FlatXmlDataSetBuilder().build(getClass().getResourceAsStream("/dataset.xml"));
}
#Transactional
#Test
public void testDeletionOfIngredients() {
(...)
}
}
Try using a DataSourceDatabaseTester like this:
public class MyTestCase {
#Autowired
private DataSource dataSource;
private IDatabaseTester dbTester;
#Before
public void init() throws Exception {
dbTester = new DataSourceDatabaseTester( getDataSource() );
dbTester.setDataSet( getDataSet() );
dbTester.onSetUp();
}
#Destroy
public void destroy() throws Exception {
dbTester.onTearDown();
}
private IDataSet getDataSet() throws Exception {
return new FlatXmlDataSet(new FileInputStream("dataset.xml"));
}
}
I have an Integration test as shown below, and I want to rollback the changes to the database after going through the test cases. Now my question is is there a way to prevent default rollback from happening after each test case and have a roll back once all the test cases are done.
After surfing a bit I found that TestNG can be helpful, but I don't want to use that. Is there any other alternative?
#TransactionConfiguration(transactionManager = "myTransactionManager", defaultRollback = true)
public class TestDependencies extends testBase {
#Test
public void testSetupData() throws SQLException, Exception{
//Some initial setup code.
}
#Test
public void testFunctionality throws Exception{
//here i will further test some more functionality
}
}
If all your test cases that require rollback are in one class, the junit #afterclass annotation will run this method after all #test methods are executed.
#AfterClass
public static void Cleanup() {
txManager.rollback();
}
txManager should be wired up to your DataSourceTransactionManager bean.
I'm using the new spring-test in the 3.1 version to run integration tests. It works really well but I can't make the session to work. My code:
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration("src/main/webapp")
#ContextConfiguration({"classpath:applicationContext-dataSource.xml",
"classpath:applicationContext.xml",
"classpath:applicationContext-security-roles.xml",
"classpath:applicationContext-security-web.xml",
"classpath:applicationContext-web.xml"})
public class SpringTestBase {
#Autowired
private WebApplicationContext wac;
#Autowired
private FilterChainProxy springSecurityFilterChain;
#Autowired
private SessionFactory sessionFactory;
protected MockMvc mock;
protected MockHttpSession mockSession;
#Before
public void setUp() throws Exception {
initDataSources("dataSource.properties");
mock = MockMvcBuilders.webAppContextSetup(wac).addFilters(springSecurityFilterChain).build();
mockSession = new MockHttpSession(wac.getServletContext(), UUID.randomUUID().toString());
}
#Test
public void testLogin() throws Exception {
// this controller sets a variable in the session
mock.perform(get("/")
.session(mockSession))
.andExpect(model().attributeExists("csrf"));
// I set another variable here just to be sure
mockSession.setAttribute(CSRFHandlerInterceptor.CSRF, csrf);
// this call returns 403 instead of 200 because the session is empty...
mock.perform(post("/setup/language")
.session(mockSession)
.param(CSRFHandlerInterceptor.CSRF, csrf)
.param("language", "de"))
.andExpect(status().isOk());
}
}
My session is empty in every request, I don't know why.
EDIT: The last assert is failing: andExpect(status().isOk());. It returns 403 instead of 200.
UPDATED ANSWER:
It seems a new method "sessionAttrs" has been added to the builder (see mvc controller test with session attribute)
Map<String, Object> sessionAttrs = new HashMap<>();
sessionAttrs.put("sessionAttrName", "sessionAttrValue");
mockMvc.perform(MockMvcRequestBuilders.get("/uri").sessionAttrs(sessionAttrs))
.andDo(print())
.andExpect(MockMvcResultMatchers.status().isOk());
OLD ANSWER:
here is a simpler solution to achieve the same result without using supporting classes, this is a snippet of my code (I don't know if these methods had been already available when Biju Kunjummen answered):
HttpSession session = mockMvc.perform(post("/login-process").param("j_username", "user1").param("j_password", "user1"))
.andExpect(status().is(HttpStatus.FOUND.value()))
.andExpect(redirectedUrl("/"))
.andReturn()
.getRequest()
.getSession();
Assert.assertNotNull(session);
mockMvc.perform(get("/").session((MockHttpSession)session).locale(Locale.ENGLISH))
.andDo(print())
.andExpect(status().isOk())
.andExpect(view().name("logged_in"));
I have done this in a somewhat roundabout manner - works though. What I did was to let Spring-Security create a session with the relevant Security attributes populated in the session and then grab that session this way:
this.mockMvc.perform(post("/j_spring_security_check")
.param("j_username", "fred")
.param("j_password", "fredspassword"))
.andExpect(status().isMovedTemporarily())
.andDo(new ResultHandler() {
#Override
public void handle(MvcResult result) throws Exception {
sessionHolder.setSession(new SessionWrapper(result.getRequest().getSession()));
}
});
SessionHolder is my custom class, just to hold the session:
private static final class SessionHolder{
private SessionWrapper session;
public SessionWrapper getSession() {
return session;
}
public void setSession(SessionWrapper session) {
this.session = session;
}
}
and SessionWrapper is another class extending from MockHttpSession, just because the session method requires MockHttpSession:
private static class SessionWrapper extends MockHttpSession{
private final HttpSession httpSession;
public SessionWrapper(HttpSession httpSession){
this.httpSession = httpSession;
}
#Override
public Object getAttribute(String name) {
return this.httpSession.getAttribute(name);
}
}
With these set, now you can simply take the session from the sessionHolder and execute subsequent methods, for eg. in my case:
mockMvc.perform(get("/membersjson/1").contentType(MediaType.APPLICATION_JSON).session(sessionHolder.getSession()))
.andExpect(status().isOk())
.andExpect(content().string(containsString("OneUpdated")));
I'm testing the CRUD operations of my DAOs in JUnit tests.
When i execute the single test, Hibernate always resets the schema and populates the DB in a known state. But when i execute multiple tests in a row, Hibernate resets the schema once, and then the data is accumulated during the execution of the tests.
This is an unexpected behavior, so I'd like to add in the #Before method of the tests a function that explicitly resets the schema to avoid the pesistence of side data created by previous tests during the execution chain.
Any tips?
Thanks
First of all, what you're doing is not unit testing, it's integration testiong.
Tips:
Use transactions and roll the back.
Consider using DBUnit (not sure how helpful it would be in your case).
Here's some relevant code from org.springframework.orm.hibernate3.LocalSessionFactoryBean.
...
Connection con = session.connection();
Dialect dialect = Dialect.getDialect(getConfiguration().getProperties());
String[] sql = getConfiguration().generateSchemaCreationScript(dialect);
executeSchemaScript(con, sql);
You could begin a transaction before and rollback the transaction each #Test method, something like this:
public class HibernateIntegrationTest {
protected static SessionFactory factory;
protected Session session;
#BeforeClass
public static void createSessionFactory() {
AnnotationConfiguration config = new AnnotationConfiguration();
// ...
config.configure();
factory = config.buildSessionFactory();
}
#AfterClass
public static void closeSessionFactory() {
if (factory != null) {
factory.close();
}
}
#Before
public void beginTransaction() {
session = factory.getCurrentSession();
session.beginTransaction();
}
#After
public void rollbackTransaction() {
if (session.getTransaction().isActive()) {
session.getTransaction().rollback();
}
if (session.isOpen()) {
session.close();
}
}
}
And extend this class in your tests classes.
public class DemoTest extends HibernateIntegrationTest {
#Test
public void createMyEntity() {
MyEntity e = new MyEntity();
//...
session.save(e);
assertNotNull(e.getId());
}
}
Note that this is not the cleanest way to do this, the code above is more for demonstration purposes.