В вашем тесте я не вижу, чтобы вы заменили webServiceClient
на смоделированную версию.
Но, во-первых, я считаю, что вам лучше не писать такой код как PaymentHandler
без внедрения зависимостей.Это может быть просто простая композиция с webServiceClient
, вводимым в PaymentHandler
.Без внедрения зависимостей он не гибкий, трудно обслуживаемый и, как следствие, не тестируемый.Представьте, например, что произойдет, если для инициализации такого поля потребуется некоторое взаимодействие с внешней системой.Как бы вы протестировали его без каких-либо библиотек, манипулирующих байт-кодом?Или как бы вы могли легко перейти с одного webServiceClient
на другой, например, с не-ssl на ssl?
Несмотря на эти хорошо известные проблемы, иногда нам приходится иметь дело со сторонним или устаревшим кодом, который мы можемне легко изменить.Но мы хотим написать тесты для кода, который взаимодействует с этим сторонним кодом.Именно по этой причине существуют классные тестовые среды.PowerMock является одним из них, и ниже приведен рабочий код, использующий его:
@RunWith(PowerMockRunner.class)
@PrepareForTest(PaymentHandler.class)
public class PaymentHandlerTest {
@Test
public void test() throws Exception {
//we don't want to initialize the PaymentHandler.class because it might cause some
//heavy undesirable initilization. E.g. if we had referred to PaymentHandler as a class
//literal here, then the webServiceClient would've been initializaed with some "real"
//instance of Client. My PaymentHandler is located in so package. You should specify your
//fully qualified class' name here
Class<?> clazz = Thread.currentThread().getContextClassLoader().loadClass("so.PaymentHandler");
//now the webServiceClient will be null once we initialize the PaymentHandler class
PowerMockito.suppress(PowerMockito.method(clazz, "createSslClient"));
Client client = mock(Client.class);
//here we initialize the PaymentHandler.class and finally mock the webServiceClient
Whitebox.setInternalState(clazz, "webServiceClient", client);
PaymentHandler paymentHandler = new PaymentHandler();
WebResource webResource = mock(WebResource.class);
WebResource.Builder builder = mock(WebResource.Builder.class);
when(client.resource(anyString())).thenReturn(webResource);
when(webResource.getRequestBuilder()).thenReturn(builder);
//take note of any(MediaType.class) instead of anyString() from your example. As in
//your PaymentHandler, MediaType is used instead of String
when(builder.type(any(MediaType.class))).thenReturn(builder);
when(builder.accept(any(MediaType.class))).thenReturn(builder);
when(builder.post(Matchers.eq(String.class), anyString())).thenReturn("{}");
paymentHandler.makePayment("payload");
}
}
В моем примере я использовал следующие зависимости:
testCompile group: 'org.powermock', name: 'powermock-api-mockito2', version: '2.0.0'
testCompile group: 'org.powermock', name: 'powermock-module-junit4', version: '2.0.0'
Это последние версии, но ранееверсии могут сделать это также