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);
    }
}
Share:
15,294
Yotus
Author by

Yotus

Software engineer at day, gamer &amp; youtuber at night

Updated on June 04, 2022

Comments

  • Yotus
    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 ?