Arquillian and PowerMock: In-container testing with PowerMock
Testing in-container applications that use EJB, CDI or other injection dependency frameworks while using PowerMock at the same time turns out to be really hard.
TestNG
Using the combination of Arquillian and PowerMock in TestNG is actually not possible at all for the following reason: According to the official PowerMock documentation[1] TestNG must be configured with the following two steps to use PowerMock. Firstly, the TestNG suite.xml must be configured to use the PowerMockObjectFactory
.
<suite name="dgf" verbose="10" object-factory="org.powermock.modules.testng.PowerMockObjectFactory">
<test name="dgf">
<classes>
<class name="com.mycompany.Test1"/>
<class name="com.mycompany.Test2"/>
</classes>
</test>
</suite>
Secondly, the test class itself must inherit from the PowerMockTestCase
class.
Furthermore, the Arquillian documentation[2] requires the test class to inherit from the Arquillian
class which is where our TestNG hopes die.
JUnit
At first glance things look rough here too because the aforementioned docs for JUnit require us to annotate or test class to run with both runners. On the one hand we have to configure the PowerMockRunner
like this:
@RunWith(PowerMockRunner.class)
public class YourTestCase {
@Test
public void test() {
// ...
}
}
And on the other hand we should configure the Arquillian
runner:
@RunWith(Arquillian.class)
public class YourTestCase {
@Test
public void test() {
// ...
}
}
Obviously only one runner can be configured at the same time.
PowerMock however has introduced a method to bootstrap itself using a JUnit rule[3] thus avoiding the JUnit TestRunner dilemma:
@RunWith(Arquillian.class)
public class YourTestCase {
@Rule
PowerMockRule rule = new PowerMockRule();
@Test
public void test() {
// ...
}
}
Apparently this is the way to go if you are using JUnit. However, even in the newest versions of PowerMock (1.5.4) this bootstrapping method seems to be working only barely. I've had trouble mocking static final classes regardless of this configuration.
Closing Thoughts
In-container testing is always integration testing and not unit testing! The injections you allow Arquillian to make always require the other units you inject to work correctly!
Consider separating your in-container-/integration-tests and out-of-container/unit-tests. Both are equally important but serve different purposes.
The latter requires no container-testing framework like Arquillian. Consider the following CDI bean:
import javax.inject.Inject;
...
@Named
@RequestScoped
public class UserProcessor {
@Inject
private UserService userService;
...
}
Unit-Testing this with PowerMock can be done easily in the following manner:
public class UserProcessorTest extends PowerMockTestCase {
private UserProcessor userProcessor;
// spy(new UserService()) for a partial mock
private UserService userService = mock(UserService.class);
@BeforeMethod
public void initMocks() {
userProcessor = new UserProcessor();
Whitebox.setInternalState(userProcessor, "userService", userService);
}
@Test
public void testUserService() {
// ...
}
}
Finally, a new in-container contestant is on the rise: Apache DeltaSpike[4]. The Apache OpenWebBeans documentation[5] outlines how to use it for in-container testing. I couldn't get it to work with CDI though :(
1. https://code.google.com/p/powermock/wiki/TestNG_usage↩
2. http://docs.jboss.org/arquillian/reference/1.0.0.Alpha1/en-US/html_single/#d0e429↩
3. https://code.google.com/p/powermock/wiki/PowerMockRule↩
4. http://deltaspike.apache.org/↩
5. http://openwebbeans.apache.org/testing_cdictrl.html↩