Hot questions for Using Enterprise JavaBeans in 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'm making my first Java EE project and can't figure how to test my EJB. I've read that Arquillian is really useful to do integration tests, but it seems like using it without Maven is difficult. Could someone tell me if there is a way to use it or a tutorial?


Answer:

Are you using any sort of dependency management tool, eg Ivy? I really recommend using one, since it makes things much easier.

That said, Arquillian does not use Maven itself, so you can use it without Maven. You will just have to figure out which dependencies you need. You can either work it out by just looking at the Maven POM files, or you could start with a sample Maven based setup and use the maven-dependency-plugin to export the libraries you need. See also https://gist.github.com/mojavelinux/2363038.

Personally I would recommend to just use a dependency management tool, be it Maven or Ivy.