Reputation: 1541
Is it possible to use some kind of mocking framework with Arquillian, or precisely how to mock injected EJBs? I know that, with using the CDI (Contexts and Dependency Injection), it is possible to inject alternatives in test. But without CDI as injection mechanism, when I'm only using EJB injection, how this is possible?
Recently I have tested my EJBs with service interface mock implementation as following:
// 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"));
This approach is possible but requires a lot of custom mock implementations. What is worse, injected instances of mocks are proxies and uses service interface. These can not be cast to mock implementation class to compare results. Only static members and methods of mock implementation can be used.
I have tested also another possibilities to set related EJBs manually. This approach has several draw-backs. It requires that target EJB of test has non-private members or setters for them. When target EJB relies on @PostConstruct lifecycle annotation, you have to call it after your manual "injection" setting. Advantage of this solution is the ability to use mock frameworks, like mockito or jMock.
Have someone an experience to share, how to test and set-up such integration test, or even use mocking frameworks in it ?
Upvotes: 16
Views: 12602
Reputation: 1375
Work with a framework, like Mockito.
Unfortunately, Arquillian does not automatically include the necessary dependencies.
You can add them in your @Deployment
function:
@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))
);
}
Then in your @Test
method you could use:
mock(MockedService.class).methodName()
This github showcase shows a way to allow auto discovery, which seems to require some setup: source
Upvotes: 1
Reputation: 1140
You may want to take a look at testfun-JEE which allows you to unit-test (not integration-test) your EJBs outside of a container. testfun-JEE takes care for injecting EJBs as well as EntityManager and some standard resource directly into your test class - references within these EJBs to other EJBs are resolved automatically.
And the coolest thing is that you can mock any dependency by simple adding a member variable to your test annotated with @Mock
- testfun-JEE will inject this mock wherever needed.
See examples in https://github.com/michaelyaakoby/testfun.
BTW, while this framework was published very recently (like today...) it is being widely used for over a year in my company.
Upvotes: 2
Reputation: 1273
This article from Oracle shows an approach to "injecting" an EJB for testing using JUnit and Mockito: http://www.oracle.com/technetwork/articles/java/unittesting-455385.html
Edit: Basically the inclusion of Mockito allows for mocking objects like EntityManager etc.:
import static org.mockito.Mockito.*;
...
em = mock(EntityManager.class);
They show the approach for EJB as well using mockito. Given an EJB:
@Stateless
public class MyResource {
@Inject
Instance<Consultant> company;
@Inject
Event<Result> eventListener;
The test can "inject" those objects:
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);
}
Note that MyResource and MyResource are in the same class path but different source folders so your tests have access to the protected fields, company
and eventListener
.
Edit:
Note: you can use FacesMockitoRunner
from JBoss (https://community.jboss.org/thread/170800) to get this done for the common JSF components and use annotations for the others (Java EE 6 with CDI enabled as a pre-requisite for this, but does not require JBoss server):
Include jsf, mockito, and jsf-mockito dependencies in maven:
<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>
Add the @RunWith
annotation to your test:
@RunWith(FacesMockitoRunner.class)
public class MyTest {
Inject common Faces objects using annotations:
@Inject
FacesContext facesContext;
@Inject
ExternalContext ext;
@Inject
HttpServletRequest request;
Mock any other objects using the annotations @org.mockito.Mock
(it appears FacesMockitoRunner
calls this behind the scenes so it may not be necessary here):
@Mock MyUserService userService;
@Mock MyFacesBroker broker;
@Mock MyUser user;
Init the Injected Mocks using the
@Before public void initMocks() {
// Init the mocks from above
MockitoAnnotations.initMocks(this);
}
Setup your test as usual:
assertSame(FacesContext.getCurrentInstance(), facesContext);
when(ext.getSessionMap()).thenReturn(session);
assertSame(FacesContext.getCurrentInstance().getExternalContext(), ext);
assertSame(FacesContext.getCurrentInstance().getExternalContext().getSessionMap(), ext.getSessionMap());
etc.
Upvotes: 2
Reputation: 1064
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.
Upvotes: 0
Reputation: 83
IMO, EJBs where not designed with testing in mind. Your alternative sounds like a good enough compromise and I'd go for it. Using mockito is a major plus and I use it even when working with CDI.
I'd use the "default" member scope and javadoc to other developers access them for testing purposes only.
Upvotes: 3