Hot questions for Using Mockito in ejb

Question:

I have the following EJB's:

PersonService.java

@Local
public interface PersonService {
    long countPersons();
}

PersonServiceImpl.java

@Stateless
public class PersonServiceImpl implements PersonService {

    @EJB
    private RemotePersonService remotePersonService;

    @Override
    public long countPersons() {
         return remotePersonService.getAllPersons().size();
    }
}

RemotePersonService.java

@Local
public interface RemotePersonService {
    List<Person> getAllPersons();
}

RemotePersonServiceImpl.Java

@Stateless
public class RemotePersonServiceImpl {
    @Override
    public List<Person> getAllPersons() {
        // Here, I normally call a remote webservice, but this is for the purpose of this question
        List<Person> results = new ArrayList<Person>();
        results.add(new Person("John"));
        return results;
    }
}

And here are my tests

AbstractTest.java

public abstract class AbstractTest {

    private InitialContext context;

    @BeforeClass(alwaysRun = true)
    public void setUp() throws Exception {
        System.setProperty("java.naming.factory.initial", "org.apache.openejb.client.LocalInitialContextFactory");

        Properties properties = new Properties();
        properties.load(getClass().getResourceAsStream("/unittest-jndi.properties"));

        context = new InitialContext(properties);
        context.bind("inject", this);

    }

    @AfterClass(alwaysRun = true)
    public void tearDown() throws Exception {
        if (context != null) {
            context.close();
        }
    }
}

PersonServiceTest.java

@LocalClient
public class PersonServiceTest extends AbstractTest {

    @EJB
    private PersonService personService;

    @Test
    public void testPersonService() {
        long count = personService.countPersons();

        Assert.assertEquals(count, 1l);
    }
}

Now, want I want to do is replace the RemotePersonService implementation in PersonServiceImpl.java by a mock using Mockito, and still have the same call in my testPersonService method.

I tried that:

PersonServiceTest.java

@LocalClient
public class PersonServiceTest extends AbstractTest {

    @Mock
    private RemotePersonService remotePersonService;

    @EJB
    @InjectMocks
    private PersonService personService;

    @BeforeMethod(alwaysRun = true)
    public void setUpMocks() {
        MockitoAnnotations.initMocks(this);

        List<Person> customResults = new ArrayList<Person>();
        customResults.add(new Person("Alice"));
        customResults.add(new Person("Bob"));

        Mockito.when(remotePersonService.getAllPersons()).thenReturn(customResults);
    }

    @Test
    public void testPersonService() {
        long count = personService.countPersons();

        Assert.assertEquals(count, 2l);
    }
}

But this doesn't work. The @Mock RemotePersonService is not injected in the PersonService, and the true EJB is still used.

How can I make this work ?


Answer:

Don't use annotations for your tests. Have a constructor that will wire in all your dependencies.

@Stateless
public class PersonServiceImpl implements PersonService {

    @EJB
    private RemotePersonService remotePersonService;

    // Let your test instantiate a mock service and wire it into your test instance using this constructor.
    public PersonServiceImpl(RemotePersonService rps) {
        this.remotePersonService = rps;
    }

    @Override
    public long countPersons() {
         return remotePersonService.getAllPersons().size();
    }
}

Create mocks and pass them to it. Your test might look like this:

@LocalClient
public class PersonServiceTest extends AbstractTest {

    @Test
    public void testPersonService() {
        RemotePersonService mockRemotePersonService = Mockito.mock(RemotePersonService.class);
        List<Person> customResults = new ArrayList<Person>();
        customResults.add(new Person("Alice"));
        customResults.add(new Person("Bob"));
              Mockito.when(mockRemotePersonService.getAllPersons()).thenReturn(customResults);
        PersonService personService = new PersonServiceImpl(mockRemotePersonService);
        long count = personService.countPersons();    
        Assert.assertEquals(count, 2l);
    }
}

Question:

I have to test method of some SLSB which invokes another method on current object(using this keyword) and I need to stub it somehow.

Consider following simplified code:

@Local
public interface SomeService{
    public int someMethod();
    public int anotherMethod();
}

@Stateless()
public class SomeServiceImpl{

    @EJB
    private SomeDAO sDAO;

    public SomeServiceImpl(){}

    public SomeServiceImpl(SomeDAO sDAO){
         this.sDAO = sDAO;
    }

    @Override
    public int someMethod(){
        int dbValue = sDAO.getSomeDBValue(); // 1st stub required here
        return dbValue + this.anotherMethod(); // 2nd stub required here
    }

    @Override
    public int anotherMethod(){
         return 5;
    }
}

To stub getSomeDBValue() method I can inject mock to this class with @Mock and @InjectMocks annotations but i can't figure out how to correctly stub anotherMethod(). To stub it I need to do it on mock object for sure, so I tried to pass reference to current object as parameter and in test just pass mocked object. For example if my method would look like this(without need to stub DAO method)..

@Override
public int someMethod(SomeServiceImpl sS){ 
    return sS.anotherMethod(); 
}

My test with manually created mocks would look like this:

@Test
public void someMethodTest() throws Exception {
    SomeServiceImpl sS = mock(SomeServiceImpl.class);
    when(sS.someMethod(any(SomeServiceImpl.class))).thenCallRealMethod();
    when(sS.anotherMethod()).thenReturn(5);
    assertEquals(5, sS.someMethod(sS));
}

Method is invoked on mock object, reference to object itself is passed as parameter and anotherMethod is stubed. It worked but it seems very ugly solution and what if need to inject mock of my DAO using annotations like this:

@RunWith(MockitoJUnitRunner.class)
public class SomeClassTest{

    @Mock
    SomeDAO sDAO;

    //@Mock //I can't use those 2 annotations at once
    @InjectMocks
    SomeServiceImpl sS; 

    @Test
    public void someMethodTest() throws Exception {
        //...   
    }
}

As I understand @InjectMocks annotation is used to indicate class where mocks annotated with @Mock should be injected, but for my ugly solution I need SomeServiceImpl to be mock as well.

Is my solution even close to correct? How I'm suppose to stub anotherMethod() to correctly test someMethod()? Is it a good idea to pass mocked instance of class, which method i test in method argument? If yes, how should I deal with creating mocks with annotations?


Answer:

You should not mock one method while testing another method on the same class. You could theoretically do that (using a Mokito spy for example).

In that sense, you are approaching this on a wrong level: you actually should not care which other methods your method under test calls within your class under test. You see, you want to test that someMethod() does fulfills its contract. If that requires a call to anotherMethod() in your production environment ... how valuable is your unit test, when it mocks anotherMethod() then?!

Another idea: you separate concerns, and move anotherMethod() part into its own class X. And then your class under test could hold an instance of X; and that instance could then be mocked.

Question:

so i am new to testing with mockito and i have looked up a couple of tutorials but i'm pretty slow to understanding. I have an endpoint that is backed with an EJB. i want to test the methods in the EJB. I've tried so many options, apparently i get a nullpointer exception. (i know what a nullpointer exception is). the mocked entity manager does not persist the objects. so it gives me a null pointer exception when i try to perform any operation on an empty list. please anyone can show me how to go about this or point me to any detailed article or tutorial i could read to facilitate my learning. thanks Guys.

this is my ejb.

@Stateless
public class CustomerHandler {

  @PersistenceContext
  private EntityManager em;

  public Response borrowMovie(List<RequestMovieDto> borrow) {
    borrow.forEach(movies -> {
        final Movie movie = findMovieByName(movies.getName());
        movie.setAvailableCopies((movie.getAvailableCopies()) - movies.getAmount());
        em.merge(movie);
    });

    return Response.ok(borrow).build();
  }

  public Movie findMovieByName(String name) {
    return em.createQuery("SELECT m FROM Movie m "
        + "WHERE m.name = :name", Movie.class)
        .setParameter("name", name)
        .getSingleResult();
  }
} 

and this is my test class

@RunWith(MockitoJUnitRunner.class)
public class MovieHandlerTest {

  @Mock
  EntityManager manager;

  private List<RequestMovieDto> request;

  @InjectMocks
  CustomerHandler handler;

  @Before
  public void setUp() {
    final Movie first = new Movie();
    first.setName("MISSION IMPOSSIBLE");
    first.setAvailableCopies(10);
    first.setIsbn("ABC788900");

    manager.persist(first);

    final Movie second = new Movie();
    first.setName("JAMES BOND");
    first.setAvailableCopies(10);
    first.setIsbn("ABC788999");

    manager.persist(second);

    final Movie last = new Movie();
    first.setName("HARRY POTTER");
    first.setAvailableCopies(10);
    first.setIsbn("ABC7882000");

    manager.persist(last);

  }

  @Test
  public void borrowMovie() {
    RequestMovieDto first = new RequestMovieDto();
    first.setName("MISSION IMPOSSIBLE");
    first.setAmount(2);

    RequestMovieDto second = new RequestMovieDto();
    second.setName("JAMES BOND");
    second.setAmount(1);

    request = Arrays.asList(first, second);
     final var response = handler.borrowMovie(request);

    assertEquals(Response.Status.OK, response.getStatusInfo().toEnum());
  }
}

Answer:

To avoid the NullPointerException, you can try adding the following code in the MovieHandlerTest class:

import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.when;

[..]

@Mock
EntityManager manager;

@Mock
TypedQuery<Movie> query;

[..]

@Before
public void setUp() {
    final Movie first = new Movie();
    first.setName("MISSION IMPOSSIBLE");
    first.setAvailableCopies(10);
    first.setIsbn("ABC788900");

    final Movie second = new Movie();
    first.setName("JAMES BOND");
    first.setAvailableCopies(10);
    first.setIsbn("ABC788999");

    final Movie last = new Movie();
    first.setName("HARRY POTTER");
    first.setAvailableCopies(10);
    first.setIsbn("ABC7882000");

    when(manager.createQuery(any(String.class), eq(Movie.class))).thenReturn(query);
    when(query.setParameter(any(String.class), any(String.class))).thenReturn(query);
    when(query.getSingleResult()).thenReturn(first, second, last);
}

But another problem arises with "Response.ok(borrow).build()"

You have 3 solutions:

  1. Refactor the code to introduce a class that executes the borrowing workflow and returns a model, then unit test this class
  2. Add a JAX-RS implementation with a test scope, such as Jersey
  3. Since you can't mock static methods with Mockito alone, you can try adding another testing library, such as PowerMockito, and mock the Reponse static method calls.

For solution 2, you can add the following maven dependency:

    <dependency>
        <groupId>org.glassfish.jersey.core</groupId>
        <artifactId>jersey-server</artifactId>
        <version>2.13</version>
        <scope>test</scope>
    </dependency>

Question:

This test works:

@RunWith(MockitoJUnitRunner.class)
public class Test1 {

    @InjectMocks private MyBean bean;       

    @Test
    public void shouldWork() {      
        bean.test("ABC");       
    }
}

@Stateless
public class MyBean {

    public String test(String s) {
        System.out.println("This is a test " + s);
        return s;
    }
}

Now, I want to inject a new EJB MyBean2 in MyBean and retest. To do that, I mock MyBean2 in Test1 as well. This is the new test:

@RunWith(MockitoJUnitRunner.class)
public class Test1 {

    @Mock MyBean2 bean2;        
    @InjectMocks MyBean bean;       

    @Test
    public void shouldWork() {
         bean.test("ABC");      
    }

}


@Stateless
public class MyBean {

    @Inject
    MyBean2 bean2;

    public String test(String s) {
        return s + bean2.test2();
    }
}


@Stateless
public class MyBean2 {
    public String test2() {
        return "DEF";
    }
}

But when I run this I get NullPointerException when bean tries to invoke the bean2 method, probably because Mockito is not recognizing the injected bean as EJB.

How to make this work?


Answer:

Take a look into the Javadoc of @InjectMocks. It states that you have to call an init of the mocks in use by calling in your case:

@RunWith(MockitoJUnitRunner.class)
public class Test1 {
    @InjectMocks
    MyBean bean;
    @Mock
    MyBean2 bean2;

    @Before
    public void init() {
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void shouldWork() {
        when(bean2.test2()).thenReturn("mocked return");
        assertThat(bean.test("ABC")).isEqualTo("ABCmocked return");
    }
}

Question:

I have a main EJB that injects a DAO EJB:

@Stateless
@LocalBean
public class MainEjb {

    @Inject
    private DaoEjb dao;

    public MyClass someMethod(int i) {
         return dao.read(i);
    }

}


@Stateless
@LocalBean
public class DaoEjb {

     public MyClass read(int i){
          // get MyClass object using jdbc
          return object;
     }
}

Now, I want to test MainEjb.someMethod() using jUnit + Mockito, injecting in the test the real MainEjb, and mocking the DaoEjb.read() method to return aMyClass` object (instead of making a jdbc call):

@RunWith(MockitoJUnitRunner.class)
public class UserBeanUnitTest {

    @InjectMocks
    private MainEjb bean;

    DaoEjb dao = mock(DaoEjb.class);


    @Test
    public void testBean() {

        MyClass object = new MyClass();
        // set object fields

        assertThat(bean.someMethod(1)).isEqualTo(object);
    }

}

The problem is that I don't know how to connect the bean and dao beans, so this doesn't work. I know I can do this with Arquillian, but I'm trying to avoid instantiating a container. Can this be done with Mockito?


Answer:

Your example worked for me. I just added a rule for dao:

@Test
public void testBean() {

    MyClass object = new MyClass();
    // set object fields
    Mockito.when(dao.read(Matchers.eq(1))).thenReturn(object);

    assertThat(bean.someMethod(1)).isEqualTo(object);
}

Question:

I am trying to create a Junit test for a method which includes ScheduleExpression. I have tried to use PowerMockito to mock it but I continue to get a error message which reads

java.lang.ClassFormatError: Absent Code attribute in method 
that is not native or abstract in class file javax/ejb/ScheduleExpression

Here is how I am using PowerMockito

 ScheduleExpression expression = PowerMockito.mock(ScheduleExpression.class);

I have tried the annotations

@RunWith(PowerMockRunner.class)
@PrepareForTest(Static.class)

but they didn't work.

I have also tried Mockito version 1.9.5 but I get the same error. Am I missing a pom dependency?

Is there a way to Mock the ScheduleExpression?


Answer:

You have to use the name of the class you want PowerMock to prepare for...

@PrepareForTest(ScheduleExpression.class)

However, in your case I don't see the need to use PowerMock because ScheduleExpression is not final, or has private or static methods you may be trying to mock.

Try just using vanilla Mockito instead of PowerMockito like this...

@RunWith(MockitoJUnitRunner.class)
...
ScheduleExpression expression = Mockito.mock(ScheduleExpression.class);

Question:

I'm woking on EJB Project, using JPA to query data. Now, I create unit test and use mockito to mock data. There is a function that I call data from criteria builder, and it's called from @PostConstruct. So if result is empty, then it will throw NoResultException. However, I am unable to run unit test to test it. Take a look on source code below:

For class RefdataUpdateDaoImpl

public class RefdataUpdateDaoImpl{
    public RefdataUpdate getSingleUpdate() {
        CriteriaBuilder cb = getCriteriaBuilder();
        CriteriaQuery<RefdataUpdate> query = cb.createQuery(RefdataUpdate.class);
        Root<RefdataUpdate> rootEntry = query.from(RefdataUpdate.class);

        CriteriaQuery<RefdataUpdate> all = query.select(rootEntry);
        TypedQuery<RefdataUpdate> allQuery = getEntityManager().createQuery(all);
        return allQuery.getSingleResult();
    }
}

In RefDataCacheBean

@PostConstruct
private void postConstruct() throws Exception{
    cachedUpdateTs = RefdataUpdateDaoImpl.getLastUpdate();
}

This is unit test

@Test(expected = NoResultException.class)
    public void testExistingRefDataUpdate() throws NoResultException{
        update.deleteRefDataUpdate();
        refdataServiceBean.getLastUpdate();
    }

So in unit test, it loads data from dataset xml. So when I run test, it supposes to throw NoResultException and test passes, but test fails and console log no entity data found.

Please help to create unit test in this case, when function's called from @PostConstruct.

Trace Logs:

javax.ejb.EJBException: javax.persistence.NoResultException: No entity found for query

Thank you and any comments will be appreciated.


Answer:

Obviously the NoResultException is nested in a EJBException so your test code should be:

@Test(expected = EJBException.class)
public void testExistingRefDataUpdate() throws NoResultException{
    update.deleteRefDataUpdate();
    refdataServiceBean.getLastUpdate();
}

Question:

I am using: Netbeans, Glassfish, Mockito

While working with Java EE for the first time I have come across the issue of trying to test methods that interact with the database using an entitymanager.

In the below code snippets I have tried to mock out the entity manager so i know that the db interaction will not be tested exactly and for this testing thats ok. But I am striggling on instansiating the UsersController because it has an injected EntityManager which is always null. I would like the EntityManager to be mocked out so I can test the rest of the method.

Below is a class that interacts with the db. NOTE this is an example methods, it is not for use in production.

@Stateless
public class UsersController {
    @PersistenceContext()
    EntityManager em;

    public User getOne() {
        em.getEntityManagerFactory().getCache().evictAll();
        User theUser = null;
        try {
            Query q = em.createNamedQuery("User.findAll");
            Collection<User> entities = q.getResultList();
            theUser = Iterables.get(entities, 1);
        }
        catch(NoResultException e){}
        em.flush();
        return theUser;
    };
}

The test file that will test this method.

@RunWith(MockitoJUnitRunner.class)
public class UsersControllerTest {

    @Mock
    private UsersController usersController;
    @Mock
    private EntityManager entityManagerMock;

    private Collection<User> mockUsersDbCollection = //...

    @BeforeClass
    public void setUpClass() {

        when(entityManagerMock.createNamedQuery("User.findAll")).thenReturn(mockUsersDbCollection);
    }

    @Test
    public void findOneTest(){
        User mockUserDbEntry = new User("1", "pa$$word", "salt", "user1", "user1@email.com", false);

        User returnedUser = null;
        returnedUser = usersController.getOne();

        assertEquals(returnedUser.getId(), "1");
    }
}

Whenever the UsersController mock is created the entityManager is always null causing issues, how can I inject the EntityManagerMock so it will work?


Answer:

You have a few issues here:

  • you are testing your UsersController, therefore it should not be mocked; rather you should use the @InjectMocks annotation on it because you want Mockito to inject your mock entity manager

  • the @BeforeClass annotation only works when applied to a static method, which in turn can only access static instance variables; in this instance I think you just need the @Before annotation

  • your entityManagerMock.createNamedQuery needs to return a mock Query object, the code you pasted should not even compile

In the end, your test should look something like:

@RunWith(MockitoJUnitRunner.class)
public class UsersControllerTest {

    @InjectMocks
    private UsersController usersController;

    @Mock
    private EntityManager entityManagerMock;

    @Mock
    private EntityManagerFactory entityManagerFactory;

    @Mock
    private Cache emfCache;

    @Mock
    private Query findAllQuery;

    private List<User> mockUsersDbCollection = //...

    @Before
    public void setUp() {
        when(entityManagerFactory.getCache())
            .thenReturn(emfCache);
        when(entityManagerMock.getEntityManagerFactory())
            .thenReturn(entityManagerFactory);
        when(entityManagerMock.createNamedQuery("User.findAll"))
            .thenReturn(findAllQuery);
        when(findAllQuery.getResultList())
            .thenReturn(mockUsersDbCollection);
    }

    @Test
    public void findOneTest(){
        mockUsersDbCollection.add(new User("1", "pa$$word", "salt", "user1", "user1@email.com", false));

        User returnedUser = usersController.getOne();

        assertEquals(returnedUser.getId(), "1");
    }
}