0

我正在使用 PowerMock 轻松模拟来模拟类的静态方法。我编写了两个测试用例,如果我独立运行,它们运行良好,但同时运行时给我一个错误。

汽车测试:

@RunWith(PowerMockRunner.class)
@PrepareForTest({ ServiceCaller.class })
public class CarTest {

  ServiceCaller           mockServiceCallerObjectToReturn;

  public CarTest() {
    PowerMock.mockStaticPartial(ServiceCaller.class, "getInstance");
    mockServiceCallerObjectToReturn = PowerMock.createMock(ServiceCaller.class);
    EasyMock.expect(ServiceCaller.getInstance()).andReturn(mockServiceCallerObjectToReturn);
  }

  @Test
  public void test1() throws IOException {
    PowerMock.reset(mockServiceCallerObjectToReturn);
    PowerMock.reset(ServiceCaller.class);

    EasyMock.expect(ServiceCaller.getInstance()).andReturn(mockServiceCallerObjectToReturn);

    EasyMock.expect(mockServiceCallerObjectToReturn.checkValidity("testDriver")).andReturn(false);

    PowerMock.replay(mockServiceCallerObjectToReturn);
    PowerMock.replay(ServiceCaller.class);

    Car car = CarFactory.getInstance().getCar();
    boolean canDrive = car.drive("testDriver");
    Assert.assertEquals(canDrive, false);

    PowerMock.verify(mockServiceCallerObjectToReturn);
    PowerMock.verify(ServiceCaller.class);
  }

  @Test
  public void test2() throws IOException {
    PowerMock.reset(mockServiceCallerObjectToReturn);
    PowerMock.reset(ServiceCaller.class);

    EasyMock.expect(ServiceCaller.getInstance()).andReturn(mockServiceCallerObjectToReturn);

    EasyMock.expect(mockServiceCallerObjectToReturn.checkValidity("testDriver")).andReturn(false);

    PowerMock.replay(mockServiceCallerObjectToReturn);
    PowerMock.replay(ServiceCaller.class);

    Car car = CarFactory.getInstance().getCar();
    boolean canDrive = car.drive("testDriver");
    Assert.assertEquals(canDrive, false);

    PowerMock.verify(mockServiceCallerObjectToReturn);
    PowerMock.verify(ServiceCaller.class);
  }
}

汽车厂:

public class CarFactory {    
  private static final String               CAR_SPRING_CONTEXT_XML = "/com/archit/mock/spring-config/CarSpringContext.xml";    
  protected static final ApplicationContext CONTEXT                = new ClassPathXmlApplicationContext(new String[] { CAR_SPRING_CONTEXT_XML });    
  private static final CarFactory           INSTANCE               = new CarFactory();    
  public static CarFactory getInstance() {
    return INSTANCE;
  }    
  public Car getCar() {
    return CONTEXT.getBean("car", Car.class);
  }    
}

车:

package com.archit.mock;
public class Car {
  private final ServiceCaller        serviceCaller;
  public Car(final ServiceCallerFactory serviceCallerFactory) {
    this.serviceCaller = serviceCallerFactory.getServiceCaller();
  }
  public boolean drive(final String driver) {
    return (serviceCaller.checkValidity(driver));
  }
}

服务调用者:

package com.archit.mock;
public class ServiceCaller {
  private static class ServiceCallerHolder {
    private static ServiceCaller INSTANCE = new ServiceCaller();
  }
  public static ServiceCaller getInstance() {
    return ServiceCallerHolder.INSTANCE;
  }
  public boolean checkValidity(final String x) {
    // Do some call
    throw new IllegalStateException("This should have been mocked");
  }
}

服务调用者工厂:

package com.archit.mock;
public class ServiceCallerFactory {
  public ServiceCaller getServiceCaller() {
    return ServiceCaller.getInstance();
  }
}

弹簧配置:

<bean name="car" class="com.archit.mock.Car">
    <constructor-arg>
        <ref bean="serviceCallerFactory" />
    </constructor-arg>
</bean>

<bean name="serviceCallerFactory" class="com.archit.mock.ServiceCallerFactory" />

错误:

java.lang.AssertionError: 
  Unexpected method call ServiceCaller.checkValidity("testDriver"):
    ServiceCaller.checkValidity("testDriver"): expected: 1, actual: 2
  at org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:44)
  at org.easymock.internal.ObjectMethodsFilter.invoke(ObjectMethodsFilter.java:85)
  at org.easymock.internal.ClassProxyFactory$MockMethodInterceptor.intercept(ClassProxyFactory.java:94)
  at com.archit.mock.ServiceCaller$$EnhancerByCGLIB$$9848ad9e.checkValidity(<generated>)
  at com.archit.mock.Car.drive(Car.java:12)
  at com.archit.mock.CarTest.test2(CarTest.java:60)
  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  at java.lang.reflect.Method.invoke(Method.java:601)
  at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:66)
  at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:312)
  at org.junit.internal.runners.MethodRoadie$2.run(MethodRoadie.java:86)
  at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:94)
  at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.executeTest(PowerMockJUnit44RunnerDelegateImpl.java:296)
  at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runBeforesThenTestThenAfters(PowerMockJUnit44RunnerDelegateImpl.java:284)
  at org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:84)
  at org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:49)
  at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.invokeTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:209)
  at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.runMethods(PowerMockJUnit44RunnerDelegateImpl.java:148)
  at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$1.run(PowerMockJUnit44RunnerDelegateImpl.java:122)
  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:120)
  at org.powermock.modules.junit4.common.internal.impl.JUnit4TestSuiteChunkerImpl.run(JUnit4TestSuiteChunkerImpl.java:102)
  at org.powermock.modules.junit4.common.internal.impl.AbstractCommonPowerMockRunner.run(AbstractCommonPowerMockRunner.java:53)
  at org.powermock.modules.junit4.PowerMockRunner.run(PowerMockRunner.java:42)
  at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
  at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
  at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
  at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
  at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
  at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

其他观察:

  • 在spring config中将对象范围作为原型工作正常。
  • 单独运行时,这两个测试都可以正常工作。
  • 基于以上2,重新设置模拟似乎是一个问题。
4

1 回答 1

1

我发现每次运行测试时,使用模拟让它们作为新对象开始时更容易。您可以通过使用 Mockito.Mock 注释将所有模拟对象作为私有变量放在顶部来实现这一点:

@Mock
private MockOneClass mockOne;
...
@Mock
private MockNClass mockN;

然后使用 JUnit Before 注释,创建某种初始化所有模拟对象的设置函数:

@Before
public void setup() {
    // initialize all the @Mock objects
    MockitoAnnotations.initMocks(this);
}

这样,在每次测试运行之前,您都会创建一个新的 Mock,然后您可以对其应用任意数量的expects功能,而不必担心清除之前测试中完成的任何旧模拟。如果您有任何您知道将提供某些特定功能的 Mock(getInstance例如静态单例模拟调用),可以在此setup函数中调用,以帮助保持 Mock 重置的测试更清晰。

我有一些测试有 10 多个测试都使用这个框架连续运行。它不仅使它更易于阅读,而且还可以非常快速地从头开始设置测试。能够在测试中复制旧的模拟设置并删除/更改某些部分要容易得多,然后必须在每次测试中维护每个模拟对象,一旦您开始进行更多测试,这将无法很好地扩展。

于 2013-08-16T19:49:24.100 回答