2017-01-18 137 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!

相关问题