Я хочу провести модульное тестирование 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.