2012-09-03 126 views
4

我想避免嘲笑类的getClass()方法,但似乎无法找到任何方法。我试图测试一个类,它将HashMap中的对象类类型存储到稍后使用的特定方法中。这方面的一个简单的例子是:用PowerMockito嘲笑getClass方法

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

看起来你不应该首先关心嘲笑getClass。你会发布关于测试本身的一些细节吗? – Vitaliy

+0

我同意,你试图断言什么? 'testMethod()'返回的是无效的,所以没有任何东西可以'assert',它看起来不像你更新传入的参数对象。因此我只能假设有一些方法调用来自里面testMethod(),你想'验证'的互动 – Brad

+0

我希望使用mockito来验证测试中发生了一些行动。我的地图实际上将类类型存储到方法中,以便我可以为实现接口的每个类调用方法,然后如果相关方法返回true(或者没有该类的方法),我想确保一些行为发生了。 –

回答

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,它会通过一个绿色的吧......哇噢!

4

你的问题实际上是getClassfinalObject,所以你不能与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。将模拟注入到您正在测试的课程中。

+0

我以为使用PowerMockito允许我存根最终的方法? –

+0

是的PowerMockito呢。我仍然在努力看到你想要测试的东西,因此我看不出这个答案是否有用。包装getClass()并不觉得正确的做法。 – Brad

+0

我想验证在给定特定场景的情况下,我以类InterfaceA(即运行)传入的类中的方法在此方法中被调用。该映射实际上将对象类类型保存到一些saftey方法中,并且根据这些方法的结果(它们返回一个布尔值)调用类中的run方法。 –

0

我也遇到类似的问题。我认为你应该添加ClassToTest。类到@PrepareForTest,因为你想模拟该类中的getClass()函数