2016-12-01 242 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。