Имитируйте последний класс и вставьте его в автоматически подключенный элемент данных и преодолейте вызов метода postConstruct

Я хочу провести модульное тестирование java-класса с автоматически подключенным конечным объектом класса, а также с другим автоматически подключенным классом, который имеет метод @PostConstruct. Хотя можно протестировать их по отдельности, я не могу объединить их вместе.

Этот вопрос является расширением вопроса о внедрении mockito mocks в Spring фасоль

Код для тестирования

public class A { 
    @Autowired
    private FinalClass serviceClient;
    @Autowired
    private ClassWithPostConstructor resourceVerifier;

    //no setters or constructors

    public String useBothFinalClassAndClassWithPostConstructor() {
        //logic to be tested
    }
}

Рабочий тестовый класс

@RunWith(PowerMockRunner.class)
@PrepareForTest(FinalClass.class)
public class ATest {
    //@org.mockito.Mock //fails to mock final class
    @org.powermock.api.easymock.annotation.Mock
    private FinalClass serviceClient;
    @org.powermock.api.easymock.annotation.Mock
    private ClassWithPostConstructor resourceVerifier;
    //other mock objects required for mocking the services

    //@InjectMocks //fails since mocking final class
    private A a;

    @Before
    public void init() {
        a = new A();
        //working snippet with setters created in A and without @Autowired here within the test
        serviceClient = PowerMock.create(FinalClass.class);
        a.setServiceClient(serviceClient);
        resourceVerifier = PowerMock.create(ClassWithPostConstructor.class);
        a.setClassWithPostConstructor(resourceVerifier);
    }

    @Test
    public void testTheMethodUsingExpectAndVerify() {
        //test the functionality here
        EasyMock.expect(serviceClient.callService()).andReturn("someMock");
        EasyMock.expect(resourceVerifier.verifyFn()).andReturn("success");
        PowerMock.replayAll();
        A.useBothFinalClassAndClassWithPostConstructor();
        PowerMock.verifyAll();
    }
}

Приведенный выше код работает с необходимостью установки в файле

Ожидаемый тестовый класс

@RunWith(PowerMockRunner.class)
@PowerMockRunnerDelegate(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"file:spring-configuration/unit-testing-config.xml"})
@PrepareForTest(FinalClass.class)
public class ATest {
    @Autowired
    private FinalClass serviceClient;
    @Autowired
    private ClassWithPostConstructor resourceVerifier;
    //other mock objects required for mocking the services

    private A a;

    @Before
    public void init() {
        a = new A();
    }

    @Test
    public void testTheMethodUsingExpectAndVerify() {
        //test the functions here
        EasyMock.expect(serviceClient.callService()).andReturn("someMock");
        EasyMock.expect(resourceVerifier.verifyFn()).andReturn("success");
        PowerMock.replayAll();
        A.useBothFinalClassAndClassWithPostConstructor();
        PowerMock.verifyAll();
    }
}

//spring-configuration/unit-testing-config.xml
//same error even on customer factory
<bean id="resourceVerifier" class="org.powermock.api.easymock.PowerMock" factory-method="createMock">
    <constructor-arg type="java.lang.Class" value="com.company...resourceVerifier" /> 
</bean>
<bean id="resourceVerifier" class="org.powermock.api.easymock.PowerMock" factory-method="createMock">
    <constructor-arg type="java.lang.Class" value="com.company...serviceClient" /> 
</bean>

Приведенный выше фрагмент издевается над finalClass, но вызывает @PostConstructor из ResourceVerifier.class - что здесь нужно сделать, чтобы преодолеть этот вызов.


Расследования

  • Можно тестировать файлы с автоматическим подключением, используя @InjectMocks, без необходимости настройки контекста Spring.
  • @InjectMock автоматически завершается ошибкой для статических и конечных полей, а в случае сбоя он также не внедряет другие макеты.
  • Можно имитировать последний класс с помощью createMock PowerMock и запустить тест с помощью PowerMockRunner и @PrepareForTest. Но для этого требуются новые ненужные сеттеры для вставки имитаторов для полей @Autowired.
  • MockitoAnnotations. @ Mock не работает вместе с PowerMock (особенно при имитации объектов конечного класса) и может быть решен с помощью EasyMock.Annotations. @ Mock
  • EasyMock и PowerMock не имеют аннотации @InjectMocks для внедрения макетов, насколько это возможно с помощью Mockito (решило бы проблему за секунды).
  • Можно внедрить автоматически подключенные компоненты Spring с помощью SpringJUnit4Runner и отдельного модульного тестирования @ContextConfiguration
  • Можно запустить один и тот же тестовый файл как с PowerMockRunner, так и с SpringJUnit4Runner с PowerMockRunnerDelegate
  • Я знаю, что метод @PostConstruct не будет выполняться автоматически, если он высмеян в коде, чем при использовании создания и внедрения Spring bean.
  • Если класс bean-компонента фабрики-оболочки написан и используется для создания макетов, он внедряется автоматически, но вместе с ним вызывает метод @PostConstruct.
  • Невозможно полагаться на Springockito, поскольку на данном этапе он ненадежен.

Но ничего из этого не сработало, поскольку сценарий использования представляет собой комбинацию всего этого.


Возможные решения

  • Удалите поля @Autowired и используйте Setter-инъекции, чтобы это было возможно, обычное издевательство с использованием PowerMock (проверено на работу) - но это соглашение, которому следуют пакеты внешней группы - i должен стараться изо всех сил придерживаться этого.
  • Или установите @Autowired для сеттеров или конструктора

Альтернативы?

Я не считаю, что классы требуют реорганизации, поскольку они служат своим целям и к тому же хорошо спроектированы.

  • Любые другие средства, которые не требуют наличия тестируемого класса - что, если бы у меня не было разрешений на изменение этого класса? т.е. решение, полностью зависящее от библиотеки тестирования.
  • Не уверены, возможно ли это с помощью PowerMockito? Не пробовал комбинировать PowerMockito с PowerMock.

person Thirukka Karnan    schedule 22.04.2016    source источник


Ответы (1)


Хм,

Не уверены, возможно ли это с помощью PowerMockito? Не пробовал комбинировать PowerMockito с PowerMock.

Мне кажется, у вас беспорядок в голове и непонимание PowerMock / Mockito и EasyMock.

Никогда не следует использовать одновременно PowerMockito и PowerMock, потому что эти два класса являются API-интерфейсом, совместимым с PowerMock, для двух разных фреймворков имитации: EasyMock и Mockito. И нет причин использовать их оба.

И, конечно, это нужно работать

//@org.mockito.Mock //fails to mock final class
@org.powermock.api.easymock.annotation.Mock
private FinalClass serviceClient;
@org.powermock.api.easymock.annotation.Mock
private ClassWithPostConstructor resourceVerifier;
//other mock objects required for mocking the services

//@InjectMocks //fails since mocking final class
private A a;

Потому что вы замедляетесь и создаете макеты через EasyMock API, но пытаетесь внедрить его с помощью Mockito Annotation.

Вам нужно выбрать только одну Mocking Framework и использовать для нее подходящий API. На данный момент Mockito + PowerMockito (API PowerMock для Mockito) лучше соответствует вашим требованиям.

Вы можете полный пример того, как это работает, на PowerMock github.

@RunWith(PowerMockRunner.class)
@PrepareForTest(FinalClass.class)
public class SpringInjectFinalClassExampleTest {

    @Mock
    private FinalClass finalClass;

    @InjectMocks
    private MyBean myBean = new MyBean();;

    @Test
    public void testInjectFinalClass() {
        final String value = "What's up?";
        when(finalClass.sayHello()).thenReturn(value);

        assertEquals(value, myBean.sayHello());

    }

}
person Arthur Zagretdinov    schedule 23.04.2016
comment
Спасибо Артуру за разъяснение основ. Я думал, что они могут работать вместе, поскольку все они mock независимо от структуры (некоторые отношения между родителями и детьми). Не могли бы вы предоставить ссылку, в которой описывается работа, а также которая отличает эти две платформы, если они у вас есть. Так будет полезнее. - person Thirukka Karnan; 25.04.2016
comment
Вы можете найти некоторую информацию на mockito wiki. Также вы можете найти множество постов со сравнением этих двух фреймворков. В вашем случае я предлагаю Mockito, потому что PowerMock уже поддерживает @InjectMocks, но аналог EasyMock @TestSubject будет поддерживаться только в следующем выпуске. - person Arthur Zagretdinov; 25.04.2016