Unit testing an EJB with Mockito, TestNG and OpenEJB
15,294
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);
}
}
![Yotus](https://i.stack.imgur.com/ffIxy.jpg?s=256&g=1)
Comments
-
Yotus about 2 years
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 ?