是否可以使用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。
让某人有分享经验,如何测试和设置此类集成测试,甚至使用模拟框架?
答案 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位于相同的类路径中,但源文件夹不同,因此您的测试可以访问受保护的字段company
和eventListener
。
编辑:
注意:您可以使用JBoss的FacesMockitoRunner
(https://community.jboss.org/thread/170800)为常见的JSF组件完成此操作,并为其他组件使用注释(启用了CDI的Java EE 6作为先决条件)这个,但不需要JBoss服务器):
在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>
将@RunWith
注释添加到测试中:
@RunWith(FacesMockitoRunner.class)
public class MyTest {
使用注释注入常见的Faces对象:
@Inject
FacesContext facesContext;
@Inject
ExternalContext ext;
@Inject
HttpServletRequest request;
使用注释@org.mockito.Mock
模拟任何其他对象(看起来FacesMockitoRunner
在幕后调用它,所以这里可能没有必要):
@Mock MyUserService userService;
@Mock MyFacesBroker broker;
@Mock MyUser user;
使用
初始化注入的模拟@Before public void initMocks() {
// Init the mocks from above
MockitoAnnotations.initMocks(this);
}
照常设置您的测试:
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会在需要的地方注入这个模拟。
答案 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))
);
}
然后在@Test
方法中,您可以使用:
mock(MockedService.class).methodName()
这个github展示了一种允许自动发现的方法,这似乎需要一些设置: source
答案 4 :(得分:0)
如果您真的想在集成测试中与模拟进行交互(例如,一个原因可能是您还没有完整的实现,或者您有一个外部系统的外观,您无法控制) ,有一种很简单的方法可以将Mockito与你的Arquillian测试结合起来,看看this example from the showcase。它实际上是扩展,但不是作为一个发布。