Hot questions for Using Mockito in session

Question:

This is the method that I want to test:

public class SelectionVariantUniqueConstraintValidatorImpl implements UniqueConstraintValidator {

    private SessionFactory sessionFactory;

    @Override
    public List<Criteria> buildValidationCriteria(Object entity, Serializable id, String[] propertyNames) {
        final SelectionVariant selectionVariant = (SelectionVariant) entity;
        final Session session = sessionFactory.getCurrentSession();
        final Criteria criteria = session.createCriteria(SelectionVariant.class);
        criteria.add(Restrictions.eq("client", selectionVariant.getClient()));
        criteria.add(Restrictions.eq("variant.variantName", selectionVariant.getVariant().getVariantName()));

        return new ArrayList<Criteria>(Arrays.asList(criteria));
    }

    @Required
    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }
}

And this is my test:

@RunWith(MockitoJUnitRunner.class)
public class SelectionVariantUniqueConstraintValidatorImplTest {

    SelectionVariantUniqueConstraintValidatorImpl selectionVariantUniqueConstraintValidator
            = new SelectionVariantUniqueConstraintValidatorImpl();

    @Mock
    private SessionFactory sessionFactory;
    @Mock
    private Session session;
    @Mock
    private Criteria criteria;

    @Before
    public void setUp() throws Exception {
        selectionVariantUniqueConstraintValidator.setSessionFactory(sessionFactory);
    }

    @Test
    public void testBuildValidationCriteria() throws Exception {
        Mockito.when(sessionFactory.getCurrentSession()).thenReturn(session);
        Mockito.when(session.createCriteria(SelectionVariant.class)).thenReturn(criteria);
        SelectionVariant selectionVariant = new SelectionVariant();
        Client client = new ClientBuilder().businessKey("HPD").description("desc").version(0).id(1L).build();
        selectionVariant.setClient(client);
        selectionVariantUniqueConstraintValidator.buildValidationCriteria(selectionVariant, null, null);
        Mockito.verify(criteria,Mockito.atLeastOnce()).add(Restrictions.eq("client", selectionVariant.getClient()));
    }
}

And this is what I see in the console:

Argument(s) are different! Wanted:
criteria.add(
    client=Client{id=1, businessKey='HPD', description='desc', version=0}
);
-> at com.innflow.ebtam.dao.hibernate.variant.SelectionVariantUniqueConstraintValidatorImplTest.testBuildValidationCriteria(SelectionVariantUniqueConstraintValidatorImplTest.java:48)
Actual invocation has different arguments:
criteria.add(
    client=Client{id=1, businessKey='HPD', description='desc', version=0}
);
-> at com.innflow.ebtam.dao.hibernate.variant.SelectionVariantUniqueConstraintValidatorImpl.buildValidationCriteria(SelectionVariantUniqueConstraintValidatorImpl.java:36)

What am I missing?

Edit:

equals method in Client.java is as follows:

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || (getClass() != o.getClass() && !(o instanceof Client))) return false;

    Client client = (Client) o;

    if (businessKey != null ? !businessKey.equals(client.getBusinessKey()) : client.getBusinessKey() != null)
        return false;

    return true;
}

Edit:

hashCode method in Client.java :

@Override
public int hashCode() {
    return businessKey != null ? businessKey.hashCode() : 0;
}

Edit:

This makes the test work:

Mockito.verify(criteria,Mockito.atLeastOnce()).add(Restrictions.eq("client", Mockito.any(Client.class)));

Answer:

The argument to criteria.add is not a Client, but the return value of Restrictions.eq, a SimpleExpression.

SimpleExpression inherits its .equals-method from Object, so since you construct a new one in your test, the two arguments are not equal, even though they contain the same Client.

A way to compare the arguments the way you intend is to implement an org.hamcrest.Matcher like this:

class SimpleExpressionMatcher extends ArgumentMatcher<SimpleExpression> {
    private SimpleExpression simpleExpression;

    public SimpleExpressionMatcher(SimpleExpression simpleExpression) {
        this.simpleExpression = simpleExpression;
    }

    @Override
    public boolean matches(Object argument) {
        @SuppressWarnings({"unchecked", "rawtypes"})
        SimpleExpression otherSimpleExpression = (SimpleExpression) argument;
        // somehow compare the SimpleExpressions, possibly like this:
        // this.simpleExpression.toString().equals(otherSimpleExpression.toString());
    }
}

Then verify like this:

 Matcher<SimpleExpression> matcher = new SimpleExpressionMatcher(Restrictions.eq("client", selectionVariant.getClient()));
 Mockito.verify(criteria).add(Mockito.argThat(matcher));

Question:

I was trying to mock hibernate session. This is the code snippet I tried:

@Before
public void setUp() {
    campaignModel = DraftTestHelper.buildDraftModel();
    if(sessionFactory != null) {
        System.out.println("Session Factory not null");
    }
    else
        System.out.println("Session Factory is null");
    session = sessionFactory.getCurrentSession();

    if(session != null) {
        System.out.print("Not null");
    }
    else 
        System.out.println("Null");

}

Mock Code:

@Mock
SessionFactory sessionFactory;

@InjectMocks
DraftCampaignModelBuilder draftBuilder;  

private DraftCampaignModel campaignModel;
private Session session;

According to console, session factory is not null. But hibernate session is null. Can anyone please help on this?

Thanks


Answer:

I think you need to write this in your @Before method:

when(sessionFactory.getCurrentSession()).thenReturn(hibernateSession);

e.g.

@Mock
private HibernateSession hibernateSession;

@Before
public void setUp() {
    when(sessionFactory.getCurrentSession()).thenReturn(hibernateSession);

    campaignModel = DraftTestHelper.buildDraftModel();
    if(sessionFactory == null) {
        System.out.println("Session Factory is null");
    }
    else {
        System.out.println("Session Factory is not null");
        session = sessionFactory.getCurrentSession();

        if(session != null) {
            System.out.print("Session is not null");
        }
        else {
            System.out.println("Session is null");
        }
    }
}

Question:

I'm getting the following NullPointerException:

Caused by: java.lang.NullPointerException at FacadeBean.createRegistration(FacadeBean.java:389)

Under FacadeBean.java:

private SessionContext context

public CreateRegistrationResponse createRegistration() {
  try {
    // snip
  } catch (DataAccessException de){
    context.setRollbackOnly(); //---------line 389
    throw new ServiceException("Error");        
  }
}

Test class

@Test(expected = ServiceException.class)
public void testCreateRegistrationError() throws ServiceException {
    doThrow(DataAccessException.class).when(mockRegistrationPeristenceImpl).create(any(Registration.class));
    facadeBeanTest.createRegistration(RegistrationFacadeMock.getCreateRegistrationRequest());
}

Could someone tell me how to mock the below line, so that I can ignore this context.setRollbackOnly();

public class FacadeBean {
  public FacadeBean() {}
  @Resource
  private SessionContext context
}

Answer:

Easiest way is to change the class to use method injection instead of field injection.

In other words, in your real class, change this:

@Resource
private SessionContext context;

into this:

private SessionContext context;

@Resource
public void setSessionContext(SessionContext sessionContext) {
 this.sessionContext = sessionContext;
}

Then, once you've done that, you can inject a mock using your unit test:

@Before
public void setUp() {
  // You probably have other code here already
  facadeBean.setSessionContext(mock(SessionContext.class));
}

You may have an issue with JAXB if you do this, though; if that happens, read this question: SessionContext Injection using @Resource annotation


If you can't change the code, you can probably access the field via reflection, doing something like this:

@Before
public void setUp() {
  Field sessionContextField = FacadeBean.class.getDeclaredField("context");
  sessionContextField.setAccessible(true);
  sessionContextField.set(beanObject, mock(SessionContext.class));
}

Question:

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?


Answer:

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

Question:

I am trying to write jUnit test case for following method.

public class MyClass {

  public static Map<String, Object> getSession() {
    Map<String, Object> session = ActionContext.getContext().getSession();
    return session;
  }
}

I followed this question and also this question and tried to mock ActionContext. But still session is null.

    public class TestClass {

        private HttpServletRequest request;
        private HttpSession session;

        @Before
        public void setUp() {
            // mock the session
            session = mock(HttpSession.class);
            // mock the request
            request = mock(HttpServletRequest);
            when(request.getSession()).thenReturn(session);

            // set the context
            Map<String, Object> contextMap = new HashMap<String, Object>();
            contextMap.put(StrutsStatics.HTTP_REQUEST, request);
            ActionContext.setContext(new ActionContext(contextMap));
        }

        @After
        public void destroyTests() {
           ActionContext.setContext(null);
        }

@Test
    public void testGetSession() {        
        Map<String, Object> session =MyClass.getSession();
        //session is null here

    }

}

Is there something I am doing wrong here ?


Answer:

Add the following code to the context map, since it's empty context created you should set the session into action context.

contextMap.put(ActionContext.SESSION, new SessionMap(request));

Question:

I have created a simple websocket application using springBoot. I am new to Mockito and I am trying to unit test the behaviour of following class with mockito and junit.

@Component
public class TextHandler extends TextWebSocketHandler {

WebSocketSession session;

@Override
public void handleTextMessage(WebSocketSession session, TextMessage message)
        throws InterruptedException, IOException {

        // send message
        if (session.isOpen()) {
            try {

                session.sendMessage(new TextMessage("Hello from the websocket"));
            } finally {
                session.close();
            }
        } else {
            System.out.println("no open session available");
        }
}

I created a stub for the session under test package as follows.

@Component
public class WebSocketSessionStub implements WebSocketSession{

@Override
public String getId() {

    return "SESSION1";
}

@Override
public URI getUri() {
    // TODO Auto-generated method stub
    return null;
}

@Override
public HttpHeaders getHandshakeHeaders() {
    // TODO Auto-generated method stub
    return null;
}

@Override
public Map<String, Object> getAttributes() {
    // TODO Auto-generated method stub
    return null;
}

@Override
public Principal getPrincipal() {
    // TODO Auto-generated method stub
    return null;
}

@Override
public InetSocketAddress getLocalAddress() {
    // TODO Auto-generated method stub
    return null;
}

@Override
public InetSocketAddress getRemoteAddress() {
    // TODO Auto-generated method stub
    return null;
}

@Override
public String getAcceptedProtocol() {
    // TODO Auto-generated method stub
    return null;
}

@Override
public void setTextMessageSizeLimit(int messageSizeLimit) {
    // TODO Auto-generated method stub

}

@Override
public int getTextMessageSizeLimit() {
    // TODO Auto-generated method stub
    return 0;
}

@Override
public void setBinaryMessageSizeLimit(int messageSizeLimit) {
    // TODO Auto-generated method stub

}

@Override
public int getBinaryMessageSizeLimit() {
    // TODO Auto-generated method stub
    return 0;
}

@Override
public List<WebSocketExtension> getExtensions() {
    // TODO Auto-generated method stub
    return null;
}

@Override
public void sendMessage(WebSocketMessage<?> message) throws IOException {
    // TODO Auto-generated method stub

}

@Override
public boolean isOpen() {
    System.out.println("isOpen");
    return true;
}

@Override
public void close() throws IOException {
    // TODO Auto-generated method stub

}

@Override
public void close(CloseStatus status) throws IOException {
    // TODO Auto-generated method stub

   }

 }

Following is my unit test class.

@RunWith(SpringRunner.class)
@SpringBootTest
public class TextHandlerTest {

@Autowired
TextHandler textHandler;

@Mock
WebSocketSessionStub ws;

@Mock
WebSocketMessage<TextMessage> webSocketMessage;

TextMessage textMsg = new TextMessage("Test Message".getBytes());

@Before
public void setUp() throws Exception {
    MockitoAnnotations.initMocks(this);
    textHandler.handleMessage(ws, textMsg);     
}


@Test
public void verifyCallToIsOpenConnection() throws Exception {

    verify(ws, times(1)).isOpen();
    System.out.println("isOpen " + ws.isOpen());

 }

}

Above test passes, however isOpen evaluates to false. Therefore I cannot verify the sendMessage method call. How can I rectify it?


Answer:

You can mock WebSocketSession and inject it into TextHandler#handleTextMessage.

/*  
 *  You don't need spring context while mocking. 
 *  Hence No need to have spring runner. 
 */
@RunWith(MockitoJunitRunner.class)
public class TextHandlerTest {

    @Test
    public void verifyCallToIsOpenConnection() {
         WebSocketSession session = mock(WebSocketSession.class);
         TextMessage textMsg = new TextMessage("Test Message".getBytes());

         when(session.isOpen()).thenReturn(true);

         TextHandler textHandler = new TextHandler();

         // Pass the mocked session object here
         textHandler. handleTextMessage(session, textMsg);

         // Now you can verify if session.sendMessage() was called or not
         verify(session, times(1)).sendMessage(textMsg);
    }
}

Question:

I'm having a problem injecting mock into one class I need for testing. I'm trying to mock a Dao class and had no problem doing so using ReflectionTestUtils in various services I'm using, however this one just does not want to work, it keeps calling the Dao class and getting errors from the database.

This is the test class:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@WebAppConfiguration
public class DedicationControllerTest extends AbstractRestTest {

    @Mock
    UserDaoImpl userDao;

    @Autowired
    @InjectMocks
    GrantedAuthoritiesLevelsHolder grantedAuthoritiesLevelsHolder;

    @Test
    public void shouldTest() throws Exception {
        //given
        String json = this.getJsonFromFile("json/my.json");

        Mockito.when(userDao.getUser(Mockito.anyString())).thenReturn(new User(1l, "mock"));
        ReflectionTestUtils.setField(grantedAuthoritiesLevelsHolder, "userDao", userDao);

        ResultActions result = mockMvc.perform(post( controllerUrl + "/action")
            .contentType(MediaType.APPLICATION_JSON_UTF8)
            .content(json));

        // then
        result
            .andExpect(status().isOk());
    }
}

And this is the class I'm trying to inject mock into:

@Component
@Scope(value="session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class GrantedAuthoritiesLevelsHolder {

    @Autowired
    private UserDao userDao;

        // some methods
}

Answer:

You will have to register mocked bean as UserDao when the context is getting loaded. You can register it as shown below. Put this in any class annotated with @Configuration

@Bean
@Primary
public UserDao UserDao() {
    return mock(UserDao.class);
}

Question:

I need to test the following controller method:

@RequestMapping(value="/addLocation")
public String addLocation(HttpServletRequest request, HttpSession session) {

    String location = (String) request.getParameter("plz_ort");
    String radius = (String) request.getParameter("umkreis");

    ((ArrayList<String>) session.getAttribute("queryTopics")).clone();        

    ...
}

Therefor I wrote this test class using Mockito and JUnit

import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import java.util.ArrayList;

import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;

public class MyControllerTest {


    @InjectMocks
    private MyController myController;

    private MockMvc mockMvc;

    @InjectMocks
    MockHttpSession session;

    @Before
    public void setup() {

        // Process mock annotations
        MockitoAnnotations.initMocks(this);

        // Setup Spring test in standalone mode
        this.mockMvc = MockMvcBuilders.standaloneSetup(MyController)
                .build();

    }

    @Test
    public void addLocation_StatusOK() throws Exception {
        session.setAttribute("queryTopics", new ArrayList<String>(0));

        this.mockMvc.perform(
                post("/addLocation")
                        .param("plz_ort", "PLZ ORT")
                        .param("umkreis", "5"))
                .andExpect(
                        status().isOk());

    }

}

As you can see I need to mock the session when running this test. When I debug through the code and stop at the line session.setAttribute("queryTopics", new ArrayList<String>(0)); the session object is org.springframework.mock.web.MockHttpSession@5583d693 (containing a attributes hash map {queryTopics=[]}), so I think that's fine. However the session object of my next breakpoint in line ((ArrayList<String>) session.getAttribute("queryTopics")).clone(); is org.springframework.mock.web.MockHttpSession@7545a27f.

At least it's the correct type (MockHttpSession) but it's got a different ID so it's actually a whole new object and thus doesn't contain the session attributes from the test.

Could you please help me out on this one?


Answer:

The mock MVC will create a spring mock request and a spring mock session and then invoke your controller method. In order to set some state in the session before the controller method is called, you need to configure the mock request using the builder:

public class MyControllerTest {

    @InjectMocks
    private MyController myController;

    private MockMvc mockMvc;

    @Before
    public void setup() {
        // Process mock annotations
        MockitoAnnotations.initMocks(this);

        // Setup Spring test in standalone mode
        this.mockMvc = MockMvcBuilders.standaloneSetup(MyController)
                .build();

    }

    @Test
    public void addLocation_StatusOK() throws Exception {
        this.mockMvc.perform(
                post("/addLocation")
                        .param("plz_ort", "PLZ ORT")
                        .param("umkreis", "5")
                        .sessionAttr("queryTopics", new ArrayList<String>(0))
                .andExpect(
                        status().isOk());
    }
}