Смоделируйте финальный класс и внедрите его в элемент данных с автоматическим связыванием и преодолите вызов метода postConstruct
Я хочу провести модульное тестирование Java-класса с конечным классом объекта с автопроводкой, а также другим классом с автопроводкой, у которого есть метод @PostConstruct. Хотя их можно проверить по отдельности, я не могу их объединить.
Этот вопрос является дополнением к вопросу о введении mockito mocks в бобовые
Код для тестирования
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 молча завершается сбоем для статических и конечных полей, а при сбое не внедряет и другие макеты.
- Можно смоделировать последний класс с помощью метода PowerMock createMock и запустить тест с PowerMockRunner и @PrepareForTest. Но для этого нужны новые ненужные сеттеры, чтобы вводить макеты для полей @Autowired.
- MockitoAnnotations. @ Mock не очень хорошо работает с PowerMock (особенно когда имитирует объекты конечного класса) и может быть решена с помощью EasyMock.Annotations. @ Mock
- EasyMock и PowerMock не имеют аннотации @InjectMocks для инъекций макетов, насколько это возможно с помощью Mockito (это решило бы проблему в секундах).
- Можно внедрять пружинные бины с автопроводкой через SpringJUnit4Runner и отдельное модульное тестирование @ContextConfiguration
- Можно запустить один и тот же тестовый файл с PowerMockRunner и SpringJUnit4Runner с PowerMockRunnerDelegate
- Я знаю, что метод @PostConstruct не будет выполняться автоматически, если в макете используется код, а не с использованием создания и внедрения Spring Bean.
- Если класс фабричного компонента-оболочки написан и используется для создания макетов, он внедряется автоматически, но вместе с ним вызывает и метод @PostConstruct.
- Невозможно зависеть от Springockito, поскольку на этом этапе он ненадежен.
Но ни один из них не работал, так как сценарий использования представляет собой комбинацию всех этих.
Возможные решения
- Удалите поля @Autowired и используйте инъекции Setter так, чтобы это было возможно путем обычного насмешки с использованием PowerMock (проверено на работоспособность) - но это соглашение, за которым следуют пакеты внешних команд - я должен стараться изо всех сил придерживаться его.
- Или установите @Autowired для сеттеров или для конструктора
Альтернативы?
Я не чувствую, что классы требуют реорганизации, поскольку они служат своим целям и также хорошо спроектированы.
- Любые другие средства, которые не требуют держать под контролем тестируемый класс. Что если у меня не было разрешений на изменение этого класса? т. е. чисто зависимое от библиотеки решение для тестирования.
- Не уверен, возможно ли это с помощью PowerMockito? Не пробовал комбинацию PowerMockito с PowerMock.
1 ответ
Хм,
Не уверен, возможно ли это с помощью PowerMockito? Не пробовал комбинацию PowerMockito с PowerMock.
Мне кажется, у вас в голове беспорядок и неправильное понимание PowerMock / Mockito и EasyMock.
Вы никогда не должны использовать в то же время PowerMockito
а также PowerMock
потому что эти два класса являются дружественным к PowerMock API для двух разных фреймворков: 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.
Вы должны выбрать только один Mocking Framework и использовать для него подходящий API. На данный момент Mockito + PowerMockito (PowerMock API для 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());
}
}