【问题标题】:Mocking EJB's with Mockito and Jersey Test Framework使用 Mockito 和 Jersey 测试框架模拟 EJB
【发布时间】:2015-11-12 18:43:44
【问题描述】:

我能够使用 jersey 测试框架来测试我的 JAX-RS restful api。但是,无论我尝试什么,我都无法正确地模拟我的 EJB。

使用 @peeskillet 提供的示例,我的 EJB 在资源中被引用的点仍然为空。

所以我知道这不是我的依赖。我相信问题在于我已经嵌套了 EJB。我确信有几个因素可能会影响此代码的行为方式,但我不确定如何解决: 1. 我试图模拟的 ApplicationService 是一个接口,我有一个 ApplicationServiceImpl 类,其中包含实际逻辑。由于我的测试不专注于该逻辑,因此我的思考过程是模拟界面应该足够了。 2. 然而,接口的实现还包含一个连接到 MongoDB 单例的 EJB。

所以我的问题: 为什么我的资源类中的接口引用始终为空,因此显然无法模拟和注入这些因素中的任何一个是否在这里发挥作用?如果是这样,我该如何解决?

如果我需要切换到直接模拟实现而不是接口,那么我是否还需要模拟 DAO ejb?

需要明确的是,只要我不调用任何依赖于通过 EJB 注入的字段/对象的方法,测试框架就可以正常工作。

我的资源类


    @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());
    }

}

我的测试用例


    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();

    }

}

我的 HK2 模拟工厂


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) {
}

}

【问题讨论】:

  • --已编辑-- 放错地方了。

标签: java ejb jax-rs testng jersey-test-framework


【解决方案1】:

我并没有给出最好的例子。虽然,Lars Juel Jensen's answer 使用的是 TestNG,而不是 JUnit,但它的设置更加简洁并提供了更好的可测试性

这是你可以做的。作为juherr mentionedApplicationServices 是不同的实例。相反,您可以做的是完全忘记Factory。只需使用模拟字段

@Mock
private ApplicationService applicationService;

@Override
protected Application configure() {
    MockitoAnnotations.initMocks(this);
    ...
}

然后忘记AbstractBinder。原因是它只适用于它知道的注释(即它不知道@EJB),除非你创建一些其他组件来让注释知道(但这太多了)。所以只需使用你的 setter 来注入它

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

现在,在每个 @Test 案例中,您可以提供完全不同的模拟实现,这意味着您的 wheh(..).then(..)s 对于每个测试案例都可能不同。无论如何,它比在工厂中保持相同的实现更灵活。

因此,在您的 @Test 方法中,只需关闭测试类的 applicationService

【讨论】:

  • 所以,谢谢你,它似乎正在工作。以前,当我尝试只使用 mockito 注释时,它似乎不起作用。这一次,我通过确保我的验证语句失败来测试它。我要求它检查我在 applicationService 上模拟的方法是否被调用了两次。它至少被调用过一次,所以我将称之为胜利。然而,灰熊返回的是 500 而不是 200,所以我想这是需要修复的其他问题。谢谢!
  • @peeskillet 您有使用内存数据库和可嵌入 EJB 容器的示例或经验吗?
【解决方案2】:
@Mock
private ApplicationService applicationService;

final ApplicationService mockedService = Mockito.mock(ApplicationService.class);

是 2 个不同的模拟实例。因此,您不能使用一个实例并检查另一个实例。

您需要找到一种方法来初始化模拟一次并在对象之间共享实例。

【讨论】:

  • 谢谢。我没有抓住那个。我一直在尝试很多不同的东西,一些残余物是其他努力留下的。还是没有解决问题。
猜你喜欢
  • 2023-03-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-23
  • 1970-01-01
  • 1970-01-01
  • 2010-10-30
相关资源
最近更新 更多