I\'d like to avoid mocking the getClass() method for a class but cannot seem to find any way around it. I\'m trying to test a class that stores objects class types in a Hash
Wow, what a headache to get this code testable. The main issues are that you can't use mock objects as key
objects into your calls to map.get(obj.getClass())
, and you're trying to invoke()
potentially mock objects for your testing. I had to refactor your class under test so that we can mock out the functionality/behaviour and be able to verify its behaviour.
So this is your new implementation to be tested with member variables decoupling the various pieces of functionailty and injected by the test class
public class ClassToTest {
MethodStore methodStore;
MethodInvoker methodInvoker;
ClassToInvoke classToInvoke;
ObjectRunner objectRunner;
public void testMethod(InterfaceA obj) throws Exception {
Method method = methodStore.getMethod(obj);
boolean ok = false;
if (method != null) {
ok = methodInvoker.invoke(method, classToInvoke, obj);
}
if (ok) {
objectRunner.run(obj);
}
}
public void setMethodStore(MethodStore methodStore) {
this.methodStore = methodStore;
}
public void setMethodInvoker(MethodInvoker methodInvoker) {
this.methodInvoker = methodInvoker;
}
public void setObjectRunner(ObjectRunner objectRunner) {
this.objectRunner = objectRunner;
}
public void setClassToInvoke(ClassToInvoke classToInvoke) {
this.classToInvoke = classToInvoke;
}
}
This is your test class that no longer requires PowerMock, because it can't mock the Method class. It just returns a NullPointerException.
public class MyTest {
@Test
public void test() throws Exception {
ClassToTest classToTest = new ClassToTest();
ClassA inputA = new ClassA();
// trying to use powermock here just returns a NullPointerException
// final Method mockMethod = PowerMockito.mock(Method.class);
Method mockMethod = (new ClassToInvoke()).getClass().getMethod("someMethod"); // a real Method instance
// regular mockito for mocking behaviour
ClassToInvoke mockClassToInvoke = mock(ClassToInvoke.class);
classToTest.setClassToInvoke(mockClassToInvoke);
MethodStore mockMethodStore = mock(MethodStore.class);
classToTest.setMethodStore(mockMethodStore);
when(mockMethodStore.getMethod(inputA)).thenReturn(mockMethod);
MethodInvoker mockMethodInvoker = mock(MethodInvoker.class);
classToTest.setMethodInvoker(mockMethodInvoker);
when(mockMethodInvoker.invoke(mockMethod,mockClassToInvoke, inputA)).thenReturn(Boolean.TRUE);
ObjectRunner mockObjectRunner = mock(ObjectRunner.class);
classToTest.setObjectRunner(mockObjectRunner);
// execute test method
classToTest.testMethod(inputA);
verify(mockObjectRunner).run(inputA);
}
}
The additional classes you require are as follows
public class ClassToInvoke {
public void someMethod() {};
}
public class ClassA implements InterfaceA {
@Override
public void run() {
// do something
}
}
public class ClassToInvoke {
public void someMethod() {};
}
public class MethodInvoker {
public Boolean invoke(Method method, Object obj, InterfaceA a) throws Exception {
return (Boolean) method.invoke(obj, a);
}
}
public class MethodStore {
Map, Method> map = new HashMap, Method>();
public Method getMethod(InterfaceA obj) {
return map.get(obj);
}
}
Put all this into your IDE and it will pass with a Green bar...woohoo!