Powermock ошибка фиктивного объекта, введенного с помощью пружины при насмешке статического объекта

Я использую PowerMock Easy Mock, чтобы издеваться над статическим методом класса. У меня написано два теста, которые, если я запускаюсь независимо, работают нормально, но выдают ошибку при одновременной работе.

CarTest:

@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));
  }
}

ServiceCaller:

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");
  }
}

ServiceCallerFactory:

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

Spring Config:

<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)

Другие наблюдения:

  • Создание объема объекта в качестве прототипа в весеннем конфиге работает нормально.
  • Оба теста в отдельности работают нормально.
  • Основываясь на выше 2, кажется, проблема с повторной настройкой макетов.

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);
}

Таким образом, перед каждым запуском теста вы создаете новый макет, который вы можете затем применить любое количество expects и функциональность, чтобы не беспокоиться о том, чтобы убрать все старые насмешки, сделанные в предыдущем тесте. Если у вас есть какой-либо Mock, который, как вы знаете, предоставит некоторую конкретную функциональность (static singleton mock getInstance звонки например) можно назвать в этом setup функция, помогающая сохранить чистоту тестов от сброса Mock.

У меня есть некоторые тесты, которые имеют более 10 тестов, которые выполняются подряд и используют эту платформу. Это не только облегчает чтение, но и позволяет очень быстро создавать тесты с нуля. Возможность копировать старые фиктивные настройки в тестах и ​​удалять / изменять определенные фрагменты намного проще, чем поддерживать каждый фиктивный объект в каждом тесте, который просто не расширяется, когда вы начинаете получать больше тестов.

Другие вопросы по тегам