开发者

How to mock services with Arquillian?

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. Adv开发者_开发百科antage 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 ?


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.


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):

  1. 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>
    
  2. Add the @RunWith annotation to your test:

    @RunWith(FacesMockitoRunner.class)
    public class MyTest {
    
  3. Inject common Faces objects using annotations:

    @Inject
    FacesContext facesContext;
    @Inject
    ExternalContext ext;
    @Inject
    HttpServletRequest request;
    
  4. 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;
    
  5. Init the Injected Mocks using the

    @Before public void initMocks() {
        // Init the mocks from above
        MockitoAnnotations.initMocks(this);
    }
    
  6. 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.


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.


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

source

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


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜