Search code examples
javaejbjax-rstestngjersey-test-framework

Mocking EJB's with Mockito and Jersey Test Framework


I was able to use the jersey test framework to test my JAX-RS restful api. However, I cannot get my EJB's to properly be mocked no matter what I try.

Using the example provided by @peeskillet, the point at which my EJB is referenced in the Resource remains null.

So I know it's not my dependencies. I believe the issue is that I have nested EJB's going on. There are a couple of factors that I'm sure may be influencing how this code is behaving, but I'm not sure how to address: 1. My ApplicationService that I am trying to mock is an interface and I have an ApplicationServiceImpl class which has the actual logic in it. Since my test is NOT focused on that logic, my thought process was mocking the interface should be sufficient. 2. However, the implementation of the interface also contains an EJB which connects to a MongoDB singleton.

So my questions: Could either of those factors be playing a role here in why the interface reference in my resource class is always null thus clearly failing to be mocked and injected? If so, how do I fix that?

If I need to switch to mocking the implementation directly instead of the interface, do I then also need to mock the DAO ejb?

To be clear, the test frame work works absolutely fine as long as I don't call any methods which rely on fields/objects injected via EJB.

My Resource Class


    @Path("/stats")
@Api(value = "stats", authorizations = {
    @Authorization(value = "apiKey", scopes = {})
})
@Stateless
@Produces({MediaType.APPLICATION_JSON})
public class StatsResource {

    private final static Logger LOG = Logger.getLogger(StatsResource.class.getName());

    @EJB
    ApplicationService applicationService;

    public void setApplicationService(ApplicationService applicationService) {
        this.applicationService = applicationService;
    }

    @GET
    public String getHere(){
        return "Got Here!";
    }

    @GET
    @Path("test")
    @ApiOperation(value = "Finds all applications",
            notes = "Returns brief display which uses a limited subset of Application.class fields",
            response = Application.class,
            responseContainer = "List<Application>"
    )
    @JsonView(Views.Brief.class)
    @Asynchronous
    public void getApplications(@Suspended
    final AsyncResponse asyncResponse) throws Exception {        
        asyncResponse.resume(doGetApplications());
    }

    private ApplicationList doGetApplications() throws Exception {
        return new ApplicationList(applicationService.getAll());
    }

}

My Test Case


    public class StatsResourceNGTest extends JerseyTestNg.ContainerPerMethodTest {
    @Mock
    private ApplicationService applicationService;

    @Override
    protected Application configure() {
        enable(TestProperties.LOG_TRAFFIC);
        enable(TestProperties.DUMP_ENTITY);

        MockitoAnnotations.initMocks(this);        
        Logger.getLogger(StatsResourceNGTest.class.getName()).log(Level.INFO, "Mock App Service: {0}", applicationService.toString());
        AbstractBinder binder = new AbstractBinder() {
            @Override
            protected void configure() {
                bindFactory(MockApplicationServiceFactory.class)
                        .to(ApplicationResource.class);
            }
        };

        ResourceConfig config = new IdentityRestApplication(true);
        config.register(binder);
        config.register(StatsResource.class);

        return config;
    }

    @Test
    public void assertMockInjectionWorked() throws Exception {
        Response response = target("/stats/test").request(MediaType.APPLICATION_JSON).get();

        verify(applicationService, times(1)).getAll();
        Logger.getLogger(ApplicationResourceNGTest.class.getName()).log(Level.INFO, "Status: {0}", response.getStatus());
        String msg = response.readEntity(String.class);
        Logger.getLogger(ApplicationResourceNGTest.class.getName()).log(Level.INFO, "Response: {0}", msg);

        Assert.assertNotNull(msg);
        response.close();

    }

}

My HK2 MockFactory


public class MockApplicationServiceFactory implements Factory<ApplicationService> {

private List<Application> appList;

@Override
public ApplicationService provide() {
    appList = new ArrayList<>();
    for (int i = 1; i < 6; i++) {
        String appname = "App " + i;
        Application app = new ApplicationBuilder()
                .setName(appname)
                .setDescription(appname + " Description")
                .setTenantId(new ObjectId())
                .createApplication();
        appList.add(app);
    }
    try {
        final ApplicationService mockedService = Mockito.mock(ApplicationService.class);
        Mockito.when(mockedService.getAll()).thenReturn(appList);
        return mockedService;
    } catch (ApplicationException ex) {
        Logger.getLogger(ApplicationResourceNGTest.class.getName()).log(Level.SEVERE, null, ex);
    } catch (Exception ex) {
        Logger.getLogger(ApplicationResourceNGTest.class.getName()).log(Level.SEVERE, null, ex);
    }
    return null;
}

@Override
public void dispose(ApplicationService t) {
}

}


Solution

  • I didn't really give the best example. Though, Lars Juel Jensen's answer uses TestNG, and not JUnit, the set up it much cleaner and provides for better testability

    Here's what you can do. As juherr mentioned, the ApplicationServices are different instances. What you can do instead, is forget the Factory altogether. Just use the mocked field

    @Mock
    private ApplicationService applicationService;
    
    @Override
    protected Application configure() {
        MockitoAnnotations.initMocks(this);
        ...
    }
    

    Then forget the AbstractBinder. The reason is that it only works for annotations it knows (i.e. it doesn't know @EJB), unless you create some other components to make the annotation know (but that is too much). So just use your setter to inject it

    StatsResource resource = new StatsResource(applicationService);
    config.register(resource);
    

    Now in each @Test case, you can provide a completely different mock implementation, meaning your wheh(..).then(..)s can be different for each test case. It's more flexible than keeping the same implementation in the factory anyway.

    So in your @Test methods, just work off the test class' applicationService.