2012-09-03 129 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()函數