我在模拟一个进行HTTP调用的RxJava函数时遇到了一些麻烦。我正在使用JUnit和Mockito。
//Customer.java extends ServiceManager
public Observable<String> getCustomerDetails(String uuidData){
String api = "http://someapi.com/" + uuidData;
return callHttps(api, getHeader(),
"",
HttpMethod.GET)
.doOnError(failure -> logger.error("Error was raised while calling Profile Save of ORCH API:"
+ failure.getMessage()))
.doOnNext(httpClientResponse -> {
logger.info("");
})
.concatMap(RxHelper::toObservable)
.reduce(Buffer.buffer(), Buffer::appendBuffer)
.map(buffer -> buffer.toString("UTF-8"))
.map(entries -> entries);
}
private MultiMap getProfileHeader(){
MultiMap headers = MultiMap.caseInsensitiveMultiMap();
headers.add("Accept","application/json");
return headers;
}
public class ServiceManager {
@Inject
@Named("httpsClient")
private HttpClient httpsClient;
private static final Logger logger = LoggerFactory.getLogger(ServiceManager.class);
public Observable<HttpClientResponse> callHttps(String url, MultiMap headers, String body, HttpMethod httpMethod) {
return Observable.create(subscriber -> {
HttpClientRequest httpClientRequest = httpsClient.requestAbs(httpMethod, url);
httpClientRequest.exceptionHandler(event -> {
logger.error("Exception was raised :" + event.getMessage());
});
httpClientRequest.headers().addAll(headers);
RxHelper
.toObservable(httpClientRequest)
.subscribe(subscriber);
httpClientRequest.end(body);
});
}
}
如何模拟callHttps
函数,以便它返回HttpClientRequest
模拟的响应。对我来说另一种方法是使用WireMock,但我想通过模拟上面的函数找到一种方法。
答案 0 :(得分:0)
假设Customer
不必是最终版,您可以使用Self-Shunt Pattern。基本上,在此模式中,您将扩展您的测试类以使用新的模拟功能覆盖方法。因此,您将扩展Customer
并覆盖callHttps()
,以便它实际上不会执行任何操作,除非记录它被调用,以便您可以验证它是否实际被调用。
请注意,我并不是说你应该像这样测试你的代码。通常使用此模式表示code that could be restructured。所以,如果可以的话,完全放弃继承。话虽如此,如果必须,请尝试使用类似于以下示例代码的内容:
public class CustomerTest {
@Mock
private Observable<HttpClientResponse> mockObservable;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
@Test
public void getCustomerDetailsCallsCallHttps() {
CustomerSelfShunt customerUnderTest = new CustomerSelfShunt();
// Call the getCustomerDetails method. Should call overridden version of
// callHttps() that sets callHttpsInvoked.
Observable<String> actualObservable = customerUnderTest.getCustomerDetails("foo");
assertEquals(this.mockObservable, actualObservable);
assertTrue(customerUnderTest.callHttpsInvoked);
}
private class CustomerSelfShunt extends Customer {
boolean callHttpsInvoked = false;
public Observable<HttpClientResponse> callHttps(String url, MultiMap headers, String body, HttpMethod httpMethod) {
// do nothing implementation, just record that this method was called.
callHttpsInvoked = true;
}
}
}
答案 1 :(得分:0)
一些选项:
示例(简化 - 在这种情况下我不得不模拟HttpClientResponse,因为我无法创建具体的实例 - 但至少你避免模拟Observable):
package com.sbp;
import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static rx.Observable.just;
import io.vertx.core.MultiMap;
import io.vertx.core.http.HttpClientResponse;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Spy;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.http.HttpMethod;
import rx.Observable;
@RunWith(MockitoJUnitRunner.class)
public class CustomerTest {
public static class ServiceManager {
public Observable<HttpClientResponse> callHttps(String url, MultiMap headers, String body,
HttpMethod httpMethod) {
return null;
}
}
public static class Customer extends ServiceManager {
public Observable<String> getCustomerDetails(String uuidData) {
return callHttps("", null, null, null).map(r -> r.getHeader(""));
}
}
@Spy
private Customer customer;
@Test
public void getCustomerDetailsCallsCallHttps() {
HttpClientResponse mockResponse = mock(HttpClientResponse.class);
when(mockResponse.getHeader(anyString())).thenReturn("test");
when(customer.callHttps(any(), any(), any(), any())).thenReturn(just(mockResponse));
String headerValue = customer.getCustomerDetails("uuid").toBlocking().single();
assertEquals("test", headerValue);
}
}
子类和覆盖 - 这是一篇优秀的文章,源自测试遗留代码簿,解释了该方法以及何时适合使用 - PHP Quickstart
中断继承模型并将ServiceManager实例注入Customer实例。然后,您将能够使用标准Mockito构造在Customer测试中模拟ServiceManager - 并且 - 您将能够独立于其子类测试ServiceManager。