Correct configuration to mock Hibernate's sessionFactory.getCurrentSession() - java

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);
}

Related

Hibernate's PreUpdateEventListener not being called in unit tests

I am trying to test my PreUpdateEventListener flow, but I cannot seem to make it work in the JUnit tests. I am not getting any error, but the code is not called.
My PreUpdateEventListener:
#Component
public class CandidateListener implements PreUpdateEventListener {
#Autowired
EntityManagerFactory entityManagerFactory;
#PostConstruct
private void init() {
HibernateEntityManagerFactory hibernateEntityManagerFactory = (HibernateEntityManagerFactory) this.entityManagerFactory;
SessionFactoryImpl sessionFactoryImpl = (SessionFactoryImpl) hibernateEntityManagerFactory.getSessionFactory();
EventListenerRegistry registry = sessionFactoryImpl.getServiceRegistry().getService(EventListenerRegistry.class);
registry.appendListeners(EventType.POST_LOAD, this);
registry.appendListeners(EventType.PRE_UPDATE, this);
}
#Override
public boolean onPreUpdate(PreUpdateEvent event) {
final Object entity = event.getEntity();
if (entity == null) return false;
// code here not being called in unit tests, but works fine on server
return false;
}
}
The test:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebAppConfiguration
#IntegrationTest
#Transactional
public class CandidateListenerTest {
#Autowired
CandidateRepository candidateRepository;
#Autowired
EntityAuditEventRepository entityAuditEventRepository;
#Autowired
SessionFactory sessionFactory;
#Test
public void testHistoryLogging() {
Candidate cand = new Candidate();
cand.setEmail("123#gmail.com");
cand.setFirstName("12");
cand.setLastName("3");
cand = candidateRepository.save(cand);
cand.setLastName("34");
candidateRepository.save(cand);
assertEquals(entityAuditEventRepository.findAll().size(), 1);
}
}
I have tried injecting the SessionFactory into the test and calling SessionFactory#flush method, but that throws No CurrentContextSession error, which I cannot seem to fix.
Finally managed to fix this.
What I have kept trying to do was to inject the EntityManager and call flush from there, but there would have been no change in the actual output (the code was still not being called).
The solution for me was inspired from here making my final test look like this:
#Test
#Transactional(propagation = Propagation.NOT_SUPPORTED)
public void testHistoryLogging() {
Candidate cand = new Candidate();
cand.setEmail("123#gmail.com");
cand.setFirstName("12");
cand.setLastName("3");
cand = candidateRepository.save(cand);
cand.setLastName("34");
candidateRepository.save(cand);
assertEquals(entityAuditEventRepository.findAll().size(), 1);
}
Basically every call is executed non-transactionally, which saves the candidate into the database after the save method from the repository is called.

Mocking a DAO that returns a list throws NPE

The DAO is simply:
#PersistenceContext
private EntityManager em;
#Override
public List<TestModel> findAll() {
return em.createNamedQuery("TestModel.findAll").getResultList();
}
and the test is:
#Mock
private EntityManager em;
#InjectMocks
#Autowired
private TestModelDao dao = new TestModelDaoImpl();
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(dao);
}
and the mock test is:
#Test
public void testFindAll() {
when(dao.findAll()).thenReturn(testModelList());
List<TestModel> testModels = dao.findAll();
assertThat(testModels, is(testModelList()));
}
but the above test throws a NullPointerException. What am I doing wrong?
Mockito returns null when the method createNamedQuery is called so you get a NullpointerException on getResultList.
You have to
TypedQuery mockedQuery = mock(TypedQuery.class);
when(em.createNamedQuery(eq("TestModel.findAll")).thenReturn(mockedQuery);
when(mockedQuery.getResultList()).thenReturn(testModelList());
But like JB Nizet said you are testing the EntityManager which was already tested by the jpa provider.
I recommend you to create an Integration Test with dbunit and an in-memory database for testing your dao.

org.hibernate.LazyInitializationException: could not initialize proxy -no Session

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

Spring mvc 3.1 integration tests with session support

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")));

How do I set up an event listener on a Hibernate 4 entity?

I'm using Spring 3.1.0.RELEASE with Hibernate 4.0.1.Final. I want to invoke an event listener on my entity bean when it is loaded from the DB, but I can't figure out what event I should be using. I load my entities in my DAO like so
#Repository("eventFeedsDao")
public class EventFeedsDaoImpl implements EventFeedsDao {
...
#Autowired
private SessionFactory sessionFactory;
...
#Override
public EventFeed findById(final Integer id) {
EventFeed eventFeed = null;
final Session session = sessionFactory.getCurrentSession();
final Criteria crit = session.createCriteria(EventFeed.class).add(Restrictions.eq("id", id));
final List<EventFeed> results = crit.list();
if (results != null && results.size() > 0) {
eventFeed = results.get(0);
} // if
return eventFeed;
} // findById
Here is how I'm trying to set up my event wiring ...
#Component
public class HibernateEventWiring {
#Autowired
private SessionFactory sessionFactory;
#Autowired
private EventMavenLoadListener listener;
#PostConstruct
public void registerListeners() {
EventListenerRegistry registry = ((SessionFactoryImpl) sessionFactory)
.getServiceRegistry().getService(EventListenerRegistry.class);
registry.getEventListenerGroup(EventType.LOAD).appendListener(listener);
}
}
and my listener class is as follows ...
#Component
public class EventMavenLoadListener implements LoadEventListener {
...
#Override
public void onLoad(final LoadEvent event, final LoadType loadType) throws HibernateException {
if(EventFeed.class.getName().equals(event.getEntityClassName())){
EventFeed entity = (EventFeed) event.getInstanceToLoad();
entity.setNetUtilsService(netUtilsService);
} // if
}
}
but the "onLoad" event is never called. What am I doing wrong? Should I be using another event?
For some reason that still eludes me, the EventType.PRE_LOAD event type was the way to go. This worked.
#Component
public class HibernateEventWiring {
#Autowired
private SessionFactory sessionFactory;
#Autowired
private EventMavenLoadListener listener;
#PostConstruct
public void registerListeners() {
final EventListenerRegistry registry = ((SessionFactoryImpl) sessionFactory)
.getServiceRegistry().getService(EventListenerRegistry.class);
registry.getEventListenerGroup(EventType.PRE_LOAD)
.appendListener((PreLoadEventListener) listener);
}
}
The load event is only triggered when loading an entity individually (session.get, initializing a proxy, fetching eager to-one associations).
The pre and post load events however follow JPA defined behavior and occur before and after the entity data is loaded regardless of how that "data load" occurred.
Its a case of bad name collision.
The preferred way in Hibernate 4, btw, to specify event listeners is through the use of an org.hibernate.integrator.spi.Integrator. See http://docs.jboss.org/hibernate/orm/4.1/devguide/en-US/html_single/#integrators

Categories

Resources