2016-12-01 35 views
1

我在模拟一个RxJava函数时会遇到一些麻烦,它会产生一些HTTP调用。我正在使用JUnit和Mockito。模拟RxJava异步http调用

//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

在我们的项目中,我们创建了真正的模拟服务,以证明精确的实时延迟以及可能的通信问题。据我所知,Mockito不能创建请求,它只会嘲笑。 – paul

+1

除了扩展ServiceManager之外,您可能更愿意将其作为字段包含进来,然后使用依赖注入来注入ServiceManager的模拟实现,从而为您提供伪造的http响应。 –

回答

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; 
    } 
    } 
} 
0

一些选项:

  1. 提供的Mockito间谍工具,以协助这种情况。 (虽然它的使用通常表明应该重组代码)

示例(简化 - 在这种情况下,我不得不嘲笑HttpClientResponse因为我无法创建一个具体的实例 - 但至少你避免嘲笑可观察) :

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); 
    } 
} 
  • 子类并覆盖 - 这里是从,解释了方法和测试遗留代码簿根深蒂固优异的制品,当它是适当的使用 - https://blog.pivotal.io/labs/labs/test-after-in-java-subclass-and-override

  • 中断继承模型并将ServiceManager的实例注入到Customer的实例中。然后,您将能够在使用标准Mockito结构的客户测试中嘲笑ServiceManager - 并且 - 您将能够独立于其子类测试ServiceManager。