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:
- Refactor the code to introduce a class that executes the borrowing workflow and returns a model, then unit test this class
- Add a JAX-RS implementation with a test scope, such as Jersey
- 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 a
MyClass` 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 managerthe
@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
annotationyour
entityManagerMock.createNamedQuery
needs to return a mockQuery
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"); } }