如何使用Arquillian模拟服务?

时间:2011-02-28 20:42:20

标签: java java-ee mocking ejb jboss-arquillian

是否可以使用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。

让某人有分享经验,如何测试和设置此类集成测试,甚至使用模拟框架?

5 个答案:

答案 0 :(得分:3)

IMO,EJB未考虑测试而设计的。你的选择听起来像一个足够好的妥协,我会去做。使用mockito是一个重要的优点,即使在使用CDI时我也会使用它。

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

答案 1 :(得分:2)

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

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

import static org.mockito.Mockito.*;

...

em = mock(EntityManager.class);

他们使用mockito展示了EJB的方法。给定一个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位于相同的类路径中,但源文件夹不同,因此您的测试可以访问受保护的字段companyeventListener


编辑:

注意:您可以使用JBoss的FacesMockitoRunnerhttps://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());
    

答案 2 :(得分:2)

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

最酷的是你可以通过简单地将一个成员变量添加到用@Mock注释的测试中来模拟任何依赖 - testfun-JEE会在需要的地方注入这个模拟。

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

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

答案 3 :(得分: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))  
        );  
}  

source

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

mock(MockedService.class).methodName()

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

答案 4 :(得分:0)

如果您真的想在集成测试中与模拟进行交互(例如,一个原因可能是您还没有完整的实现,或者您有一个外部系统的外观,您无法控制) ,有一种很简单的方法可以将Mockito与你的Arquillian测试结合起来,看看this example from the showcase。它实际上是扩展,但不是作为一个发布。