Arquillian and Mocks

Although it’s a bit contradictory to talk about Arquillian and Mocks sometimes you may need to skip some operations which are not relevant to your tests, for example:

  • Replicate data in legacy databases, here is somewhat a limitation of arquillian persistence extension where you can’t have multiple datasources but in general you don’t want to test data replication although if you have the legacy datasource configured in the test server you can simple fire a native query to see if data was replicated there.
  • If you use in memory databases such as H2 (which is somewhat a form of fake but we use it a lot ;)) you may not have some operations, functions or procedures which are available in the real application database so you may want to skip those calls in tests.
  • Skip CMS, such as Alfresco, calls in tests.
  • avoid start a mail service during tests
  • Skip In memory datagrids, such as Hazelcast, initialization during tests

In my case most of the issues are due to the fact that i can’t replicate all my infrastructure in tests, maybe with arquillian docker integration they can be solved without mocks but another interesting fact is that the components i’m faking slow down my tests and sometimes their relevance to my business logic is low.

So if you have faced any situation where you need to mock components in arquillian integration tests here is how i am dealing with that, the source code is available here: https://github.com/rmpestano/arquillian-mocks, the examples are quite simple, if you know alternatives don’t hesitate to comment here or send a pull request .

Here is some code:

MyBeanImpl.java


@RequestScoped
public class MyBeanImpl implements MyBean {

    private boolean alive = false;

    @PostConstruct
    public void init(){
       alive = true;
    }

    @Override
    public boolean someSlowOperation() {
        try {
            Thread.currentThread().sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return true;
    }

    @Override
    public boolean isAlive() {
        return alive;
    }
}

and the integration test:


@RunWith(Arquillian.class)
public class MyBeanTest {

  @Inject
  MyBeanImpl realBean;

  @Deployment
  public static WebArchive createDeployment() {
        WebArchive war = ShrinkWrap.create(WebArchive.class, "test.war")
                .addClasses(MyBeanImpl.class, MyBean.class)
                .addAsWebInfResource("test-beans.xml", "beans.xml");
        return war;
    }

  @Test
  public void shouldRunSlowWithRealBean() {
        long start = System.currentTimeMillis();
        assertTrue(realBean.someSlowOperation());
        long executionTime = (System.currentTimeMillis() - start);
        assertTrue(executionTime >= 10000);//should take at least 10000ms
    }

 } 

Now here are some ways to mock the bean slow call.

1 – Using a mock framework:
You can use a mock framework like Mockito to fake method calls, to do that you need to add mockito to test deployment and then you can mock the slow method call:


@RunWith(Arquillian.class)
public class MyBeanTest {

    //@Mock //to use @Mock see auto discover extension: https://github.com/arquillian/arquillian-showcase/blob/master/extensions/autodiscover
    @Inject
    MyBeanImpl realMockedBean;

    @Before
    public void setupMock() {
        realMockedBean = Mockito.mock(MyBeanImpl.class);//real bean replaced by mocked one
        Mockito.when(realMockedBean.someSlowOperation()).thenReturn(true);
    }

    @Deployment
    public static WebArchive createDeployment() {
        WebArchive war = ShrinkWrap.create(WebArchive.class, "test.war")
                .addClasses(MyBeanImpl.class, MyBean.class)
                .addAsWebInfResource("test-beans.xml", "beans.xml");
         MavenResolverSystem resolver = Maven.resolver();
         war.addAsLibraries(resolver.loadPomFromFile("pom.xml").resolve("org.mockito:mockito-all:1.10.8").withTransitivity().asSingleFile());
        return war;
    }
 
    @Test
    public void shouldRunFastWithMockedBean() {
        long start = System.currentTimeMillis();
        assertTrue(realMockedBean.someSlowOperation());
        assertTrue((System.currentTimeMillis() - start) < 10000);
    }
}

Note that this approach have some limitations:

  1. If the mocked bean is injected into another (real)bean you will have to set the mocked bean into the real one.
  2. When you use Mock(SomeBean.class) CDI doesn’t manage the bean anymore meaning that you will have to fake all behaviour you need in tests.

2 – CDI alternatives:

Just create an alternative implementation of the real bean and enable it in test-beans.xml:

MyBeanAlternative.java


@Alternative
public class MyBeanAlternative implements MyBean {

    @Override
    public boolean someSlowOperation() {
         return true;
    }
}

 

add the alternative bean to the test deployment and inject MyBean interface:


@RunWith(Arquillian.class)
public class MyBeanTest {

    @Inject
    MyBean myAlternativebean;

    @Deployment
    public static WebArchive createDeployment() {
        WebArchive war = ShrinkWrap.create(WebArchive.class, "test.war")
                .addClasses(MyBeanImpl.class, MyBean.class, MyBeanAlternative.class)
                .addAsWebInfResource("test-beans.xml", "beans.xml");

             return war;      }

    @Test
    public void shouldRunFastWithAlternativeBean() {
        long start = System.currentTimeMillis();
        assertTrue(myAlternativebean.someSlowOperation());
        assertTrue((System.currentTimeMillis() - start) < 5000);//should run faster than 10000
    }
}

This is a good approach that solves the first limitation of previous approach but still doesn’t solve second limitation.

3 – CDI Bean Specialization

For bean specialization you dont need to implement an interface but just extend the real bean and provide new implementation to the method you want to fake:


@Specializes
public class MyBeanSpecialization extends MyBeanImpl {

    @Override
    public boolean someSlowOperation()  {
        return true;
    }
}

 

and now in the test you can inject the real bean but dont forget to add the bean specializition on test deployment:

@RunWith(Arquillian.class)
public class MyBeanTest {

@Inject
MyBeanImpl realBean;

 @Deployment
 public static WebArchive createDeployment() {
     WebArchive war = ShrinkWrap.create(WebArchive.class, "test.war")
             .addClasses(MyBeanImpl.class, MyBean.class, MyBeanSpecialization.class)
             .addAsWebInfResource("test-beans.xml", "beans.xml");
    return war;
    }

 @Test
 public void shouldRunFastWithSpecializedBean() {
        long start = System.currentTimeMillis();
        assertTrue(realBean.someSlowOperation());
        long executionTime = (System.currentTimeMillis() - start);
        assertTrue(executionTime < 5000);//should run faster than 10000
    }
}

This approach solve both mentioned limitations.

4 – Bytecode manipulation

For the bytecode manipulation i will use arquillian-byteman extension where you can override bean methods through byte code weaving:

@RunWith(Arquillian.class)
public class MyBeanTest {

   @Inject
   MyBeanImpl realBean;

   @Deployment
   public static WebArchive createDeployment() {
       WebArchive war = ShrinkWrap.create(WebArchive.class, "test.war")
              .addClasses(MyBeanImpl.class, MyBean.class)
              .addAsWebInfResource("test-beans.xml", "beans.xml");
       return war;
    }

    @Test
    @BMRule(
      name = "fake method call", targetClass = "MyBeanImpl",
      targetMethod = "someSlowOperation",
      action = "return true;")
    public void shouldRunFastWithBytecodeManipulatedBean() {
        long start = System.currentTimeMillis();
        assertTrue(realMockedBean.someSlowOperation());
        assertTrue(realMockedBean.isAlive());
        assertTrue((System.currentTimeMillis() - start) < 5000);
    }

 

I make it work only in managed container, you need to add this vmArg to arquillian.xml:

<container qualifier="wildfly-managed">
        <configuration>
            <property name="outputToConsole">true</property>
            <property name="javaVmArguments">-Xmx512m -XX:MaxPermSize=256m
                -Djboss.modules.system.pkgs=com.sun.tools.attach,org.jboss.byteman -Xbootclasspath/a:${java.home}/../lib/tools.jar
            </property>
            <property name="allowConnectingToRunningServer">true</property>
    </configuration>
   <extension qualifier="byteman">
        <property name="autoInstallAgent">true</property>
        <property name="agentProperties">org.jboss.byteman.verbose=false</property>
    </extension>

I have tested only with wildfly application server.

Conclusion

If somehow you need mocks in your integration tests i recommend option 3 – Bean specialization cause option 1 and 2 you’ll need to reimplement all bean methods/behaviour and option 4 doesn’t work on all containers yet.

Advertisements

2 thoughts on “Arquillian and Mocks

  1. Very inspiring! Thanks for sharing your ideas! Limitation 1.1 may be removed using a producer so a mock created by the producer will be injected at every injection point (and may be reset and initialized differently for subsequent tests).

    Liked by 1 person

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s