Using PowerMock to write unit test, mocked method fails to invoke - java

I recently learn to use PowerMock to write unit tests for a class called Module which extends class Base. They look like this.
public class Base {
protected final static ServiceA serviceA;
protected final static ServiceB serviceB;
static {
serviceA = ServiceA.getInstance();
serviceB = ServiceB.getInstance();
}
}
public class Module extends Base {
public DataA methodA() {
return serviceA.getDataA();
}
public DataB methodB() {
return serviceB.getDataB();
}
}
My unit tests look like this:
#PowerMockIgnore("javax.management.*")
#RunWith(PowerMockRunner.class)
#PrepareForTest({Module.class, ServiceA.class, ServiceB.class})
public class ModuleTest {
private Module module;
#Mock
private ServiceA serviceA;
#Mock
private ServiceB serviceB;
#Before
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
PowerMockito.mockStatic(ServiceA.class);
PowerMockito.when(ServiceA.getInstance).thenReturn(serviceA);
PowerMockito.mockStatic(ServiceB.class);
PowerMockito.when(ServiceB.getInstance).thenReturn(serviceB);
module = new Module();
// I spy it because it has other methods I need to mock
module = PowerMockito.spy(module);
}
#Test
public void methodATest() {
DataA dataA = new DataA();
PowerMockito.when(serviceA.getDataA()).thenReturn(dataA);
DataA data = module.methodA();
assertEquals(dataA, data);
}
#Test
public void methodBTest() {
DataB dataB = new DataB();
PowerMockito.when(serviceB.getDataB()).thenReturn(dataB);
DataB data = module.methodB();
assertEquals(dataB, data);
}
}
Everything looks straightforward but when I run ModuleTest, the methodBTest() doesn't pass. It seems that PowerMockito.when(serviceB.getDataB()).thenReturn(dataB) doesn't work and makes the real serviceB.getDataB() method invoked. So assertEquals(dataB, data) throws org.junit.ComparisonFailure.
If I put the methodBTest() before methodATest(), the methodATest() doesn't pass. Same reason.
If I put PowerMockito.when(serviceA.getDataA()).thenReturn(dataA) and PowerMockito.when(serviceB.getDataB()).thenReturn(dataB) in the setup(), everything works perfectly.
This borders me all day. Is there anyone knowing why this is happening and how to resolve it? I need the mocking statement written in the respective test methods because I may change the returned values.

Here's a solution involving (almost) no changes
#PowerMockIgnore("javax.management.*")
#RunWith(PowerMockRunner.class)
#PrepareForTest({Module.class, ServiceA.class, ServiceB.class})
public class ModuleTest {
private Module module;
private static ServiceA serviceA = Mockito.mock(ServiceA.class);
private static ServiceB serviceB = Mockito.mock(ServiceB.class);
#BeforeClass
public static void oneTimeSetup() throws Exception {
PowerMockito.mockStatic(ServiceA.class);
PowerMockito.when(ServiceA.class, "getInstance").thenReturn(serviceA);
PowerMockito.mockStatic(ServiceB.class);
PowerMockito.when(ServiceB.class, "getInstance").thenReturn(serviceB);
}
#Before
public void setup() throws Exception {
module = new Module();
// I spy it because it has other methods I need to mock
module = PowerMockito.spy(module);
}
#Test
public void methodATest() {
DataA dataA = new DataA();
Mockito.when(serviceA.getDataA()).thenReturn(dataA);
DataA data = module.methodA();
assertEquals(dataA, data);
}
#Test
public void methodBTest() {
DataB dataB = new DataB();
Mockito.when(serviceB.getDataB()).thenReturn(dataB);
DataB data = module.methodB();
assertEquals(dataB, data);
}
}
What was changed (and why):
In Base: serviceA and serviceB are changed to protected (Module can not access if private)
used "proper" (AFAIK) syntax for PowerMockito.when(ServiceA.class, "getInstance").thenReturn(serviceA);
used a #BeforeClass and made serviceA and serviceB static to "bypass" static initialization in Base
Tested with Junit 4.12, PowerMockito 1.6.2.
Note: it's also possible to leverage #SuppressStaticInitializationFor to achieve the same goal:
#SuppressStaticInitializationFor(value = "so46196071.Base") // suppress the static in Base (note this is my package name)
#PowerMockIgnore("javax.management.*")
#RunWith(PowerMockRunner.class)
#PrepareForTest({Module.class, ServiceA.class, ServiceB.class})
public class ModuleBisTest {
private Module module;
#Mock
private ServiceA serviceA;
#Mock
private ServiceB serviceB;
#Before
public void setup() throws Exception {
// MockitoAnnotations.initMocks(this); /* this is not needed => done by the runner */
PowerMockito.mockStatic(ServiceA.class);
PowerMockito.when(ServiceA.class, "getInstance").thenReturn(serviceA);
PowerMockito.mockStatic(ServiceB.class);
PowerMockito.when(ServiceB.class, "getInstance").thenReturn(serviceB);
module = new Module();
Whitebox.setInternalState(Base.class, "serviceA", serviceA); // set serviceA in Base "by hand"
Whitebox.setInternalState(Base.class, "serviceB", serviceB); // set serviceB in Base "by hand"
// I spy it because it has other methods I need to mock
module = PowerMockito.spy(module);
}
// ...

Related

Mocking constructor using PowerMockito doesn't work

I want to test a method which creates an object of another class using it's parameterized constructor. Even though I've mocked the constructor of MyClass, it makes the third party library which is in constructor implementation and results in the error. I'm using Mockito/PowerMockito.
public class MyClass{
private MyObj obj;
MyClass (String profile)
{
//some 3rd party library call
obj = thridPartyLib.someMethod(profile);
}
public String someMethod(){
return obj.someExternalCall();
}
}
Class which I want to test
public class ClassTobeTested{
public void execute(){
//some code
// ......
MyClass myClass = new MyClass(profile);
myclass.someMethod();
}
}
What I tried so far - classUnderTest.execute() ends up calling the thridPartyLib.someMethod(profile); which is part of MyClass constructor.
#RunWith(PowerMockRunner.class)
#PrepareForTest(MyClass.class)
public class ClassTobeTestedTest {
private MyClass mockMyClass;
private ClassTobeTested classUnderTest;
#Before
public void init() {
classUnderTest = new ClassTobeTested();
mockMyClass = PowerMockito.mock(MyClass.class);
}
#Test
public void testExecute(){
PowerMockito.whenNew(MyClass.class)
.withArguments(Mockito.any())
.thenReturn(mockMyClass);
classUnderTest.execute();
}
}
Your code will work only if you are working with a spy or mock of classUnderTest. Try this. This should work
#RunWith(PowerMockRunner.class)
#PrepareForTest( {MyClass.class, ClassTobeTested.class })
public class ClassTobeTestedTest {
private MyClass mockMyClass;
private ClassTobeTested classUnderTest;
#Before
public void init() {
classUnderTest = spy(new ClassTobeTested());
mockMyClass = PowerMockito.mock(MyClass.class);
}
#Test
public void testExecute() throws Exception {
PowerMockito.whenNew(MyClass.class)
.withArguments(Mockito.any())
.thenReturn(mockMyClass);
classUnderTest.execute();
}
}
The pain might suggest another approach. Consider injecting a Factory into ClassTobeTested which knows how to create an instance of MyObj. For example:
class MyObjFactory {
MyObj create(String profile) {
return new MyClass(profile);
}
}
then
public class ClassTobeTested {
private final MyObjFactory factory;
public ClassTobeTested(MyObjFactory factory) {
this.factory = factory;
}
public void execute(){
//some code
// ......
// MyClass myClass = new MyClass(profile);
MyClass myClass = factory.create(profile);
myclass.someMethod();
}
}
so the unit test becomes simpler with only having to mock the Factory and have it return a mocked MyClass instance. Then it's simple to verify myclass.someMethod() was invoked as expected.

Inject string into class using Guice for JUnit test

I have a situation where I need to test a function but the class has injected String value like this:
public class SomeClass{
#Inject
#Named("api")
private String api;
public Observable<String> get(String uuidData){
//do something with "api" variable
}
}
Now how do I inject this from my JUnit test case? I am also using Mockito but it's not allowing me to mock primitive types.
It looks like there are two options here:
Option 1: Set up injection in the #Before of your JUnit test
//test doubles
String testDoubleApi;
//system under test
SomeClass someClass;
#Before
public void setUp() throws Exception {
String testDoubleApi = "testDouble";
Injector injector = Guice.createInjector(new Module() {
#Override
protected void configure(Binder binder) {
binder.bind(String.class).annotatedWith(Names.named("api")).toInstance(testDouble);
}
});
injector.inject(someClass);
}
Option 2: Refactor your class to use constructor injection
public class SomeClass{
private String api;
#Inject
SomeClass(#Named("api") String api) {
this.api = api;
}
public Observable<String> get(String uuidData){
//do something with "api" variable
}
}
Now your #Before method will look like this:
//test doubles
String testDoubleApi;
//system under test
SomeClass someClass;
#Before
public void setUp() throws Exception {
String testDoubleApi = "testDouble";
someClass = new SomeClass(testDoubleApi);
}
Out of the two options, I would say the second is preferable. You can see it leads to much less boiler-plate and the class can be tested even without Guice.

Jersey 2 inject dependencies into unit test

I have a controller like this
#Path("/")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public class AccountController implements CRUDController<Long, Account> {
private AccountDao accountDao;
private AccountService accountService;
#Inject
public AccountController(AccountDao accountDao, AccountService accountService) {
this.accountDao = accountDao;
this.accountService = accountService;
}
...
I'm injecting AccountDao and AccountService using
ResourceConfig config = new ResourceConfig()
.packages("controller", "exception")
.register(new MyDIBinder());
Where MyDIBinder is contains all the bindings (e.g
AccountDaoImpl accountDaoImpl = new AccountDaoImpl();
bind(accountDaoImpl).to(AccountDao.class);
)
Now I want to write a unit test for this controller, is it possible to inject the whole AccountController instance with all of it's transitive dependencies into the test?
Something like
#Inject
AccountController accountController;
You can use the main IoC container, and just explicitly inject the test class. Jersey uses HK2 as its DI framework, and its IoC container is the ServiceLocator, which has a method inject(anyObject) that can inject any objects with dependencies that are in its registry.
For example you could do something like
public class InjectionTest {
#Inject
private TestController controller;
#Before
public void setUp() {
final Binder b = new AbstractBinder() {
#Override
public void configure() {
bindAsContract(TestController.class);
}
};
final ServiceLocator locator = ServiceLocatorUtilities.bind(new TestBinder(), b);
locator.inject(this);
}
#Test
public void doTest() {
assertNotNull(controller);
String response = controller.get();
assertEquals("Hello Tests", response);
}
}
The ServiceLocatorUtilities class is a helper class that allows us to easily create the ServiceLocator, and then we just call inject(this) to inject the InjectionTest.
If it seems repetitive to do this for all your controller tests, you may want to create an abstract base test class. Maybe something like
public abstract class AbstractControllerTest {
protected ServiceLocator locator;
private final Class<?> controllerClass;
protected AbstractControllerTest(Class<?> controllerClass) {
this.controllerClass = controllerClass;
}
#Before
public void setUp() {
final AbstractBinder binder = new AbstractBinder() {
#Override
public void configure() {
bindAsContract(controllerClass);
}
};
locator = ServiceLocatorUtilities.bind(new TestBinder(), binder);
locator.inject(this);
}
#After
public void tearDown() {
if (locator != null) {
locator.shutdown();
}
}
}
Then in your concrete class
public class TestControllerTest extends AbstractControllerTest {
public TestControllerTest() {
super(TestController.class);
}
#Inject
private TestController controller;
#Test
public void doTest() {
assertNotNull(controller);
assertEquals("Hello Tests", controller.get());
}
}
If you spent some more time, I'm sure you could come up with a better abstract test class design. It was the first thing that came to mind for me.
Note: For anything request scoped, you mayb need to just mock it. When running the unit tests, there is no request context, so the test will fail.
See Also:
Using Jersey's Dependency Injection in a Standalone application
HK2 documentation
import org.glassfish.hk2.api.ServiceLocator;
import org.glassfish.hk2.utilities.ServiceLocatorUtilities;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
#BeforeClass
public static void doTest() {
ServiceLocator serviceLocator = ServiceLocatorUtilities.bind(new AbstractBinder() {
#Override
protected void configure() {
bindAsContract(YourClass1.class);
bindAsContract(YourClass2.class);
bindAsContract(YourClass3.class);
}
});
YourClass1 yourClass1 = serviceLocator.getService(YourClass1.class);
...

Mockito Mock run Autowired class Object

I have below class scenario. While testing MyTestableClass, I wish to process Autowired class.
I would like to mock only variable in AutoWired class.
sample class is as below-
public class MyTestableClass {
#Autowired
private MyServiceClass service;
public void handleError(){
...
service.doSomething();
}
}
public class MyServiceClass {
#Autowired
private JMSChannel channel;
public void doSomething(){
System.out.println("Inside Service class");
.....
channel.isAvailable();
.....
}
}
#RunWith(MockitoJUnitRunner.class)
public class MyTestableClassTest {
private MyTestableClass testClass= new MyTestableClass();
private JMSChannel channel;
#Before
public void init(){
channel= mock(JMSChannel.class);
when(channel.isAvailable()).thenReturn(Boolean.TRUE);
}
#Test
public void test(){
testClass.handleError();
}
}
For example, Console should give me "Inside Service class" before returning true.
Thanks in Advance !
You need to create and instance of your service (or a mock of it) and set its channel to your mocked one, then set MyTestableClass#service to this one. Something like:
#Before
public void init(){
channel= mock(JMSChannel.class);
when(channel.isAvailable()).thenReturn(Boolean.TRUE);
MyServiceClass service = new MyServiceClass();
ReflectionTestUtils.setField(service, "channel", channel);
myTestableClass = new MyTestableClass();
ReflectionTestUtils.setField(myTestableClass, "service", service);
}
with ReflectionTestUtils from spring-test (NB: you can use a setter instead)

Mockito vs #Configurable

I'm trying to write JUnit tests for some code that is normally managed with Spring.
Let's say I have this:
#Configurable
public class A {
#Autowired MyService service;
public void callA() { service.doServiceThings(); }
}
I can write a test for this class using Mockito and PowerMock like this:
#RunWith(PowerMockRunner.class)
public class ATest {
#Spy MyService service = new MyService();
#Before void initMocks() { MockitoAnnotations.initMocks(this); }
#Test void test() {
#InjectMocks A a = new A(); // injects service into A
a.callA();
//assert things
}
}
But now I run into a case when some other class constructs instances of A:
public class B {
public void doSomething() {
A a = new A(); // service is injected by Spring
a.callA();
}
}
How do I make service get injected into instances of A created inside a B method?
#RunWith(PowerMockRunner.class)
public class BTest {
#Spy MyService service = new MyService();
#Before void initMocks() { MockitoAnnotations.initMocks(this); }
#Test testDoSomething() {
B b = new B();
// is there a way to cause service to be injected when the method calls new A()?
b.doSomething();
// assert things
}
}
Field injection is bad but still there's one thing that you can do to easily stub that A instantiation (or maybe I misunderstood sth). Make B have an AFactory injected via constructor.
public class B {
private final AFactory aFactory;
public B(AFactory aFactory) {
this.aFactory=aFactory;
}
public void doSomething() {
A a = aFactory.getA();
a.callA();
}
}
And then you can create a Mock of aFactory and inject it to B via constructor.

Categories