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?
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.