2017-01-18 14 views
2

我是Mockito的新手,並且遇到了花費我大量時間的問題。以下是我的問題陳述和可執行代碼。PowerMock:用不同參數嘲弄同一方法的多個調用行爲異常

問題

每當我試圖嘲弄來自同樣的方法多種行爲根據不同的參數的Mockito/powermockito使用最後我在一個單一的test.Below測試定義的行爲是我的榜樣,Service類有一個靜態foo方法是從我的方法(我想測試)調用不同參數的次數。

它拋出ClassCastException,想投BResponseAResponse,因爲我最後磕碰如果BResponse而在ClassUnderTest.execute()我第一次調用foo要求AResponse

示例代碼

package poc.staticmethod; 

import static org.mockito.Matchers.any; 
import static org.powermock.api.mockito.PowerMockito.mockStatic; 
import static org.powermock.api.mockito.PowerMockito.when; 

import org.junit.Before; 
import org.junit.Test; 
import org.junit.runner.RunWith; 
import org.mockito.InjectMocks; 
import org.powermock.core.classloader.annotations.PrepareForTest; 
import org.powermock.modules.junit4.PowerMockRunner; 

import lombok.AllArgsConstructor; 
import lombok.Data; 

@RunWith(PowerMockRunner.class) 
@PrepareForTest(PocStaticTest.Service.class) 
public class PocStaticTest { 


    @InjectMocks 
    ClassUnderTest c = new ClassUnderTest(); 

    @Before 
    public void beforeTest() throws Exception { 
    mockStatic(Service.class); 
    } 

    @Test 
    public void myTest() { 
    when(Service.foo(any(), new ARequest(any(), "A"))).thenReturn(new AResponse(1, "passed")); 
    when(Service.foo(any(), new ARequest(any(), "2A"))) 
     .thenReturn(new AResponse(2, "passed")); 
    when(Service.foo(any(), new BRequest(any(), "B"))) 
     .thenReturn(new BResponse(112, "passed")); 

    c.execute(); 
    } 

    public class ClassUnderTest { 
    public void execute() { 
     AResponse ar = (AResponse) Service.foo("A1", new ARequest(1, "A")); 
     AResponse ar2 = (AResponse) Service.foo("A2", new ARequest(2, "2A")); 
     BResponse br = (BResponse) Service.foo("B1", new BRequest(1, "B")); 
    } 
    } 

    public static class Service { 
    public static Object foo(String firstArgument, Object obj) { 
     return null; 
    } 
    } 

    @Data 
    @AllArgsConstructor 
    public class ARequest { 
    public Integer num; 
    public String name; 
    } 

    @Data 
    @AllArgsConstructor 
    public class AResponse { 
    public Integer error; 
    public String message; 
    } 

    @Data 
    @AllArgsConstructor 
    public class BRequest { 
    public Integer num; 
    public String name; 
    } 

    @Data 
    @AllArgsConstructor 
    public class BResponse { 
    public Integer error; 
    public String message; 
    } 

} 

例外:

java.lang.ClassCastException: poc.staticmethod.PocStaticTest$BResponse cannot be cast to poc.staticmethod.PocStaticTest$AResponse 
    at poc.staticmethod.PocStaticTest$ClassUnderTest.execute(PocStaticTest.java:44) 
    at poc.staticmethod.PocStaticTest.myTest(PocStaticTest.java:39) 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 
    at java.lang.reflect.Method.invoke(Method.java:483) 
    at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:68) 
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:316) 
    at org.junit.internal.runners.MethodRoadie$2.run(MethodRoadie.java:89) 
    at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:97) 
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.executeTest(PowerMockJUnit44RunnerDelegateImpl.java:300) 
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.executeTestInSuper(PowerMockJUnit47RunnerDelegateImpl.java:131) 
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.access$100(PowerMockJUnit47RunnerDelegateImpl.java:59) 
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner$TestExecutorStatement.evaluate(PowerMockJUnit47RunnerDelegateImpl.java:147) 
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.evaluateStatement(PowerMockJUnit47RunnerDelegateImpl.java:107) 
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.executeTest(PowerMockJUnit47RunnerDelegateImpl.java:82) 
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runBeforesThenTestThenAfters(PowerMockJUnit44RunnerDelegateImpl.java:288) 
    at org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:87) 
    at org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:50) 
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.invokeTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:208) 
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.runMethods(PowerMockJUnit44RunnerDelegateImpl.java:147) 
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$1.run(PowerMockJUnit44RunnerDelegateImpl.java:121) 
    at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:34) 
    at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:44) 
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.run(PowerMockJUnit44RunnerDelegateImpl.java:123) 
    at org.powermock.modules.junit4.common.internal.impl.JUnit4TestSuiteChunkerImpl.run(JUnit4TestSuiteChunkerImpl.java:121) 
    at org.powermock.modules.junit4.common.internal.impl.AbstractCommonPowerMockRunner.run(AbstractCommonPowerMockRunner.java:53) 
    at org.powermock.modules.junit4.PowerMockRunner.run(PowerMockRunner.java:59) 
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86) 
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) 
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459) 
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675) 
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382) 
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192) 

回答

1

使用eq()匹配您的自定義對象,並通過這樣做改變你的功能嘲弄會看起來臨客:

when(Service.foo(any(), eq(new ARequest(1, "A")))).thenReturn(new AResponse(1, "passed")); 
when(Service.foo(any(), eq(new ARequest(2, "2A")))).thenReturn(new AResponse(2, "passed")); 
when(Service.foo(any(), eq(new BRequest(1, "B")))).thenReturn(new BResponse(112, "passed")); 

你應該在你Request對象指定參數,並從裏面取出any()

另一種選擇是寫你的答案和檢查類型裏面,如:

when(mock.foo(anyString(), anyObject())).thenAnswer(
    invocation -> { 
     Object argument = invocation.getArguments()[1]; 
     if (argument.equals(new ARequest(1, "A"))) { 
      return new AResponse(1, "passed"); 
     } else if (argument.equals(new ARequest(2, "2A"))) { 
      return new AResponse(2, "passed"); 
     } else if (argument.equals(new BRequest(1, "B"))) { 
      return new BResponse(112, "passed"); 
     } 
     throw new InvalidUseOfMatchersException(
      String.format("Argument %s does not match", argument) 
     ); 
    } 
); 
+0

感謝@ZeeshanBilal它爲我工作:) –

+0

歡迎@KhalidShah。 –

0

猜測:我覺得你有這些嘲諷規格的一種錯誤的認識。

您寫道:

when(Service.foo(any(), new ARequest(any(), "A"))) 

但是,這是否真的有意義嗎?你想說:當調用foo()時,第一個參數並不重要。第二個必須是一些ARequest對象。更具體地說,你想說:一個ARequest對象,其中忽略其比較其他「數字」部分。但是:那是不是事情會如何解決。你必須提供一個真實對象,而不是「spec'ed」!

我的意思是:後來,在運行時,模擬框架看到foo()被調用,然後開始尋找匹配的參數。但是模擬框架應該如何理解你想匹配包含任意數字的ARequest對象呢?

換句話說:我假設Lombok @Data會生成equals方法。那些當然會比較這些類別中的所有元素。

所以,我的解決方案可能是使用特定的請求對象,像

when(Service.foo(any(), new ARequest(1, "A"))) 

來然後確保數字ID總是進來爲1

或者,你可以嘗試生成只比較請求的「名稱」部分的equals()方法。

長話短說:我認爲你正在得到這些when-specifications錯誤的語義。

我認爲發生的事情是new ARequest(any(), "A")只是創建一些樣ARequest對象,然後PowerMockito將嘗試爲等於;其結果可能一直是「假」的。

最後:希望你明白你在混淆兩個字節碼的操作框架有一定的機會造成離奇的問題?當PowerMock(ito)引起奇怪的失敗時(因爲它以某種方式改變你的字節碼),我見過很多場景;然後你在上面添加lombok的東西?你甚至會嘲笑靜態調用?因此,我個人的建議:看看是否有機會將該方法轉換爲某種實例方法(甚至可以將自己的包裝放在靜態調用中),以便在此處使用普通的Mockito而不是PowerMockito!

相關問題