16

是否可以在 Arquillian 中使用某种模拟框架,或者确切地说如何模拟注入的 EJB?我知道,通过使用 CDI(上下文和依赖注入),可以在测试中注入替代方案。但是如果没有 CDI 作为注入机制,当我只使用 EJB 注入时,这怎么可能呢?

最近我用服务接口模拟实现测试了我的 EJB,如下所示:

// Service inteface 
public interface Audit {
   void audit(String info);
}

// Mock implementation
@Stateless
public class MockAuditBean implements Audit {

    public static String lastInfo = null;

    @Override
    public void audit(String info) {
        this.lastInfo = info;
    }
}

// assert in test
assertTrue(MockAuditBean.lastInfo.contains("dummy"));

这种方法是可行的,但需要大量自定义模拟实现。更糟糕的是,注入的模拟实例是代理并使用服务接口。这些不能转换为模拟实现类来比较结果。只能使用模拟实现的静态成员和方法。

我还测试了手动设置相关 EJB 的另一种可能性。这种方法有几个缺点。它要求测试的目标 EJB 具有非私有成员或设置器。当目标 EJB 依赖于 @PostConstruct 生命周期注释时,您必须在手动“注入”设置后调用它。此解决方案的优点是能够使用模拟框架,如 mockito 或 jMock。

有没有人分享经验,如何测试和设置这样的集成测试,甚至在其中使用模拟框架?

4

5 回答 5

3

IMO,EJB 在设计时没有考虑到测试。你的替代方案听起来是一个足够好的妥协,我会去的。使用 mockito 是一个主要优点,即使在使用 CDI 时我也会使用它。

我会使用“默认”成员范围和 javadoc 让其他开发人员访问它们仅用于测试目的。

于 2011-05-16T23:41:49.567 回答
2

Oracle 的这篇文章展示了一种使用 JUnit 和 Mockito “注入” EJB 进行测试的方法: http ://www.oracle.com/technetwork/articles/java/unittesting-455385.html

编辑:基本上包含 Mockito 允许模拟 EntityManager 等对象:

import static org.mockito.Mockito.*;

...

em = mock(EntityManager.class);

他们展示了 EJB 的方法以及使用 mockito。给定一个 EJB:

@Stateless
public class MyResource {
 @Inject
 Instance<Consultant> company;

 @Inject
 Event<Result> eventListener;

测试可以“注入”这些对象:

public class MyResourceTest {

 private MyResource myr;
 @Before
 public void initializeDependencies(){
 this.myr = new MyResource();
 this.myr.company = mock(Instance.class);
 this.myr.eventListener = mock(Event.class);
 }

请注意,MyResource 和 MyResource 位于相同的类路径中,但源文件夹不同,因此您的测试可以访问受保护的字段,company并且eventListener.


编辑:

注意:您可以使用FacesMockitoRunnerJBoss ( https://community.jboss.org/thread/170800 ) 为常见的 JSF 组件完成此操作,并为其他组件使用注释(启用 CDI 的 Java EE 6 作为先决条件这个,但不需要 JBoss 服务器):

  1. 在 maven 中包含 jsf、mockito 和 jsf-mockito 依赖项:

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-core</artifactId>  
            <version>1.9.5</version> 
            <scope>test</scope>
        </dependency>
        <dependency>
          <groupId>org.jboss.test-jsf</groupId>
          <artifactId>jsf-mockito</artifactId>
          <version>1.1.7-SNAPSHOT</version>
          <scope>test</scope>
        </dependency>
    
  2. @RunWith注释添加到您的测试中:

    @RunWith(FacesMockitoRunner.class)
    public class MyTest {
    
  3. 使用注解注入常见的 Faces 对象:

    @Inject
    FacesContext facesContext;
    @Inject
    ExternalContext ext;
    @Inject
    HttpServletRequest request;
    
  4. 使用注释模拟任何其他对象@org.mockito.Mock(它似乎FacesMockitoRunner在幕后调用它,所以这里可能没有必要):

    @Mock MyUserService userService;
    @Mock MyFacesBroker broker;
    @Mock MyUser user;
    
  5. 使用

    @Before public void initMocks() {
        // Init the mocks from above
        MockitoAnnotations.initMocks(this);
    }
    
  6. 像往常一样设置你的测试:

    assertSame(FacesContext.getCurrentInstance(), facesContext);
    when(ext.getSessionMap()).thenReturn(session);
    assertSame(FacesContext.getCurrentInstance().getExternalContext(), ext);
    assertSame(FacesContext.getCurrentInstance().getExternalContext().getSessionMap(), ext.getSessionMap());
    

等等

于 2013-01-25T18:13:58.373 回答
2

您可能想看看testfun-JEE,它允许您在容器外对 EJB 进行单元测试(而不是集成测试)。testfun-JEE 负责将 EJB 以及 EntityManager 和一些标准资源直接注入到您的测试类中——这些 EJB 中对其他 EJB 的引用会自动解析。

最酷的是,您可以通过简单地将成员变量添加到带有注释的测试中来模拟任何依赖@Mock项 - testfun-JEE 将在需要的地方注入此模拟。

请参阅https://github.com/michaelyaakoby/testfun中的示例。

顺便说一句,虽然这个框架是最近才发布的(就像今天......),但它在我的公司被广泛使用了一年多。

于 2013-11-01T23:25:56.640 回答
1

使用框架,例如 Mockito。

不幸的是,Arquillian 不会自动包含必要的依赖项。您可以将它们添加到您的@Deployment函数中:

@Deployment  
public static WebArchive deploy()  
{  
  return ShrinkWrap.create(WebArchive.class)  
        .addAsLibraries( // add maven resolve artifacts to the deployment  
            DependencyResolvers.use(MavenDependencyResolver.class)  
            .artifact("org.mockito:mockito-all:1.8.3")  
            .resolveAs(GenericArchive.class))  
        );  
}  

资源

然后在您的@Test方法中,您可以使用:

mock(MockedService.class).methodName()

这个 github 展示展示了一种允许自动发现的方法,这似乎需要一些设置: source

于 2014-01-30T15:31:05.747 回答
0

If you really want to interact with mocks in your integration tests (for instance one reason might be that you don't have a full blown implementation yet or you have an facade to external systems which you don't have control over), there is quite an easy way to integrate Mockito with your Arquillian tests, have a look at this example from the showcase. It's actually extension on its own, but not released as one.

于 2012-10-22T09:54:44.163 回答