Hot questions for Using Mockito in jboss arquillian

Top 10 Java Open Source / Mockito / jboss arquillian

Question:

I am working on a Java EE 7 (on wildfly 9.0.2) application and I stumbled on an article http://www.oracle.com/technetwork/articles/java/intondemand-1444614.html. Mainly about:

Premature Extensibility Is the Root of Some Evil

This makes sense in certain cases I have encountered. I have changed some classes to a no interface view. The implementation itself is not a problem, testing however is.

For example I have these 2 classes.

@Stateless
public class SomeBean {
     public String getText()
     {
         return "Test text";
     }
}

And

@Stateless
public class SomeOtherBean {
    @Inject
    private SomeBean someBean;

    public String getText()
    {
        return someBean.getText();
    }
}

I want somehow that the someBean property is overwritten with preferably a mocked object. Without altering the SomeBean and SomeOtherBean class. I have tried some examples, but they didn't work for example: https://github.com/arquillian/arquillian-showcase/tree/master/extensions/autodiscover/src/test/java/org/jboss/arquillian/showcase/extension/autodiscover

Has anyone encountered this before and have a solution?


Answer:

I ended up using 2 solutions.

Solution 1: Use mockito for internal or smaller tests

For testing a particular class Mockito is really useful, as it supports dependency injection.

@RunWith(MockitoJUnitRunner.class)
public class SomeOtherBeanTest {
    @Mock
    private SomeBean someBean;

    @InjectMocks
    private SomeOtherBean someOhterBean;

    @Before
    public void setUp() {
        Mockito.when(someBean.getText()).thenReturn("Overwritten!");
    }

    @Test
    public void testGetText() throws Exception {
        assertEquals("Overwritten!", someOhterBean.getText());
    }
}

Solution 2: Use @Produces and @Alternatives for mocking external services (e.g. mocking OAuth2 server) or larger test (e.g. integration testing)

First I create a new @Alternative annotation:

@Alternative
@Stereotype
@Retention(RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.TYPE})
public @interface CDIMock {}

Then add this as stereotype to the arquillian beans.xml deployment:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
       bean-discovery-mode="all">
    <alternatives>
        <stereotype>com.project.CDIMock</stereotype>
    </alternatives>
</beans>

After that create a new @Producer method in a seperate class:

public class SomeBeanMockProducer {
    @Produces @CDIMock
    public static SomeBean produce() {
        SomeBean someBean = Mockito.mock(SomeBean.class);
        Mockito.when(someBean.getText()).thenReturn("mocked");

        return someBean;
    }  
}

Add the SomeBeanMockProducer class to the arquillian deployment and you should have it working.

An alternative to this solution is using @Specializes and extending the SomeBean implementation. In my opinion this doesn't give me enough control like the @Alternative + Mocking solution (@CDIMock in my example).

For example, lets say I SomeBean has methods that calls remote servers. If I add a method to this and forget to @override this in the @Specializes class it will make a real remote call, this won't be the case with Mocking.

Question:

If you go to Arquillian's home page it says: No more mocks., but if I have EJB 1 that injects EJB 2 and I want to test EJB 1, how do I simulate EJB 2 if I don't mock it? Is Arquillian used for integration and not unit testing? Why is it a bad practice to mock in Arquillian and what's the alternative?


Answer:

Arquillian is indeed used for integration testing and not unit testing.

It's somewhat of a bad practice to mock extensively in Arquillian, exactly for this reason. It largely defeats the purpose of using Arquillian in the first place.

That said, mocking in Arquillian for the situation you described is reasonably easy. As the default mode in Arquillian is for you to provide micro archives build with shrinkwrap, you can simply replace EJB2 with a mock version when creating your archive.

For instance:

@RunWith(Arquillian.class)
public class MyTest {

    @Deployment
    public static Archive<?> deployment() {
        return ShrinkWrap.create(JavaArchive.class, "test.jar")
            .addClass(com.real.EJB1.class)
            .addClass(com.example.mock.EJB2.class)
        ;
    }

    // ..
}

Since EJB1 would not depend on the actual class type of EJB2 but only on its (simple) name or interface, you can just swap it out when creating your test.

Question:

I am trying to add mockito to my arquillian tests (with ShrinkWrap), like so:

    @Deployment
    public static Archive<?> createDeployment() {
        JavaArchive archive = ShrinkWrap.create(JavaArchive.class, "test.jar")
                .addPackage(BeanClass.class.getPackage())
                .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml")
                ;

        Archive[] libs = Maven.resolver()
                .loadPomFromFile("pom.xml")
                .resolve(
                        "org.mockito:mockito-all"
                )
                .withTransitivity()
                .as(JavaArchive.class);

        for (Archive lib : libs) {
            archive = archive.merge(lib);
        }

        return archive;
    }

I am using Mockito to overwrite with @Alternative. But when I add the line archive = archive.merge(lib) I am getting the Exception:

Caused by: java.lang.ClassNotFoundException: org.apache.tools.ant.Task

Or I will get

Caused by: java.lang.ClassNotFoundException: org.mockito.asm.signature.SignatureVisitor

Has anyone else experienced this too?

UPDATE: Some extra information, I am trying to test this with a wildfly embedded container: pom.xml

<dependencies>
                <dependency>
                    <groupId>org.wildfly</groupId>
                    <artifactId>wildfly-arquillian-container-embedded</artifactId>
                    <scope>test</scope>
                </dependency>

                <dependency>
                    <groupId>org.wildfly</groupId>
                    <artifactId>wildfly-embedded</artifactId>
                    <scope>test</scope>
                </dependency>

                <dependency>
                    <groupId>org.jboss.arquillian.extension</groupId>
                    <artifactId>arquillian-transaction-jta</artifactId>
                    <scope>test</scope>
                </dependency>
            </dependencies>

Answer:

I finally found the solution that I have needed. I have found a solution by including ant dependency. The problems started when I needed to use other test libraries like cucumber. I am now testing with an EAR deployment which has resolved my problems:

    @Deployment
    public static Archive<?> createDeployment() {
        final JavaArchive ejbJar = ShrinkWrap
                .create(JavaArchive.class, "ejb-jar.jar")
                .addClass(NewSessionBean.class)
                .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml");

        final WebArchive testWar = ShrinkWrap.create(WebArchive.class, "test.war")
                .addClass(NewSessionBeanTest.class)
                .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml")

                ;

        Archive[] libsArchives = Maven.resolver()
                .loadPomFromFile("pom.xml")
                .resolve("org.mockito:mockito-all")
                .withTransitivity()
                .as(JavaArchive.class);

        testWar.addAsLibraries(libsArchives);
        final EnterpriseArchive ear = ShrinkWrap.create(EnterpriseArchive.class)
                .setApplicationXML("META-INF/test-application.xml")
                .addAsModule(ejbJar)
                .addAsModule(testWar);

        return ear;
    }

And my test-application.xml

<application>
    <display-name>ear</display-name>
    <module>
        <ejb>ejb-jar.jar</ejb>
    </module>
    <module>
        <web>
            <web-uri>test.war</web-uri>
            <context-root>/test</context-root>
        </web>
    </module>
</application>

Question:

So I have a service class.

public class OrganisationService {
    public List<Organisation> findAllForeignOrganisations() {
        // a few rows of JPQL-code that searches the database
        return myCriteria.getResultList();
    }
    //...Other methods that return Lists with other types of organisations...
}

Then I have a class which I want to test. What I want to test is, when this following class's method getAllowedOrganisations is given certain parameter (OrganisationType.FOREIGN in this case), uses the aforementioned method findAllForeignOrganisation() to search for a List of organisations.

public class OrganisationSelectionOptions {

@Inject OrganisationService os;

public List<Organisation> getAllowedOrganisations(Assignment a) {
    OrganisationType type = a.getOrganisationType();
    return giveOrganisationListWithType(type);
}

private List<Organisation> giveOrganisationListWithType(OrganisationType type) {
    List<Organisation> organisations;
    if (type == OrganisationType.FOREIGN) {
        organisations = os.findAllForeignOrganisations();
    }
    // ...some other organisations types on if-clauses...
    return organisations;
    }
}

}

I made a JUnit test to test this. I have to create mocks of these aforementioned classes because they access database and currently I can't access the database in tests and would like to avoid implementing a database structure to tests.

@RunWith(Arquillian.class)
@PrepareForTest(Organisation.class)
public class OrganisationSelectionTest  {
    @Test
    public void testWithForeignType() throws RreflectionUtilException {
        // ...code to create a dummy Assignment with FOREIGN type   organisation...

    mockedOrganisationService = mock(OrganisationService.class);
    mockedOrganisationSelectionOptions = mock(OrganisationSelectionOptions.class);


    }
}

Now I tried Mockito's verify to somehow test, if mockedOrganisationSelectionOptions.giveOrganisationListWithType(dummyAssignmentWithForeignTypeOrganisation) would call os.findAllForeignOrganisations(); to no avail.

I'm a clueless beginner on creating tests. Any help would be vastly appreciated and rewarded, thank you. I'm ready to answer any comments/questions to give more detail on the issue.


Answer:

This can be done via multiple ways but I will go over one. First, because you use @Inject to get your instance of OrganisationService, then I will not mess around with mocking the new OrganisationService() constructor call. Instead we can setup a getter method for the OrganisationService, then mock it.

Add a getter in OrganisationSelectionOptions .

OrganisationService getOrganisationService(){
    return os;
}

Edit your giveOrganisationListWithType method to use our new getter.

organisations = getOS().findAllForeignOrganisations();

In your unit test mock an instance of OrganisationService. There are two ways to do this.

method one: add the following in ToimeksiannonYhtiovalintaTest.

@Mock 
private OrganisationService mockOS;

method two: instantiate your mockOS in the method doing the testing.

OrganisationService mockOS = mock(OrganisationService.class);

For method one to work you need to include..

@RunWith(MockitoJUnitRunner.class)

in ToimeksiannonYhtiovalintaTest. This is a class level annotation.

Next we need to spy the object being tested.

OrganisationSelectionOptions toTest = spy( new OrganisationSelectionOptions ());

then mock our new getter method.

when(toTest.getOrganisationService()).thenReturn(mockOS);

There you go.

EDIT: added...

Edit your giveOrganisationListWithType method to use our new getter.

organisations = getOS().findAllForeignOrganisations();

Question:

I was tring to use mockito with arquillian. I used with simple JUnit and it runs fine although i am trying to figure out the deployment for arquillian which is where i think the problem is !!! Can anyone help me to figure out ?

Thank you.

Here is the link to my junit file and pom file


Answer:

When you're using external libraries in your tests, you must make sure those libraries have been added to the microdeployment.