我想避免模拟类的getClass()方法,但似乎无法找到它的任何方法。我正在尝试测试一个将HashMap中的对象类类型存储到以后要使用的特定方法的类。一个简短的例子是:
public class ClassToTest {
/** Map that will be populated with objects during constructor */
private Map<Class<?>, Method> map = new HashMap<Class<?>, Method>();
ClassToTest() {
/* Loop through methods in ClassToTest and if they return a boolean and
take in an InterfaceA parameter then add them to map */
}
public void testMethod(InterfaceA obj) {
final Method method = map.get(obj.getClass());
boolean ok;
if (method != null) {
ok = (Boolean) method.invoke(this, obj);
}
if (ok) {
obj.run();
}
}
public boolean isSafeClassA(final ClassA obj) {
// Work out if safe to run and then return true/false
}
public boolean isSafeClassB(final ClassB obj) {
// Work out if safe to run and then return true/fals
}
}
public interface InterfaceA {
void run()
}
public class ClassA implements InterfaceA {
public void run() {
// implements method here
}
}
public class ClassB implements InterfaceA {
public void run() {
// implements method here
}
}
然后我有一个看起来像这样的JUnit测试:
@RunWith(PowerMockRunner.class)
@PrepareForTest({ClassA.class})
public class ClassToTestTest {
private final ClassToTest tester = new ClassToTest();
@Test
public void test() {
MockGateway.MOCK_GET_CLASS_METHOD = true;
final ClassA classA = spy(new ClassA());
doReturn(ClassA.class).when(classA).getClass();
MockGateway.MOCK_GET_CLASS_METHOD = false;
tester.testMethod(classA);
verify(classA).run();
}
}
我的问题是虽然在test()方法中有classA.getClass();将返回ClassA,一旦在测试者的testMethod()方法内,它仍然返回ClassA $ EnhancerByMockitoWithCGLIB $ ...类,所以我的对象总是空的。
有什么办法可以解决这个问题,或者我需要做些什么才能解决这个问题?
提前致谢。
答案 0 :(得分:5)
您的问题实际上是getClass
final
Object
,因此您无法使用Mockito将其存根。我想不出一个好方法。您可以考虑一种可能性。
编写一个具有单一方法的实用程序类
public Class getClass(Object o){
return o.getClass();
}
并重构您正在测试的类,以便它使用此实用程序类的对象,而不是直接调用getClass()
。然后,可以使用特殊的包私有构造函数或使用setter方法注入实用程序对象。
public class ClassToTest{
private UtilityWithGetClass utility;
private Map<Class<?>, Object> map = new HashMap<Class<?>, Object>();
public ClassToTest() {
this(new UtilityWithGetClass());
}
ClassToTest(UtilityWithGetClass utility){
this.utility = utility;
// Populate map here
}
// more stuff here
}
现在,在您的测试中,模拟对象和存根getClass
。将模拟注入您正在测试的类中。
答案 1 :(得分:4)
key
对象中的模拟对象用于对map.get(obj.getClass())
的调用,并且您尝试invoke()
可能会模拟测试对象。我不得不重构你的测试类,以便我们可以模拟功能/行为并能够验证它的行为。
所以这是你的新实现,用成员变量进行测试,解耦各种各样的functionailty并由测试类注入
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;
}
}
这是您的测试类,不再需要PowerMock,因为它can't mock the Method class。它只返回一个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);
}
}
您需要的其他课程如下
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<Class<?>, Method> map = new HashMap<Class<?>, Method>();
public Method getMethod(InterfaceA obj) {
return map.get(obj);
}
}
将所有这些放入你的IDE中,它将通过一个绿色条...哇哦!
答案 2 :(得分:0)
我也遇到过类似的问题。我认为你应该将ClassToTest.class添加到@PrepareForTest,因为你想模拟该类中的getClass()函数