[英]Integration Testing Spring SseEmitters
我一直在尋找有關如何最好地測試返回 SseEmitters 的 Spring MVC 控制器方法的提示。 我的想法很短,但有一個反復試驗的解決方案,可以測試異步、線程化的行為。 下面是示例代碼,只是為了演示概念,可能有一兩個錯字:
控制器類:
@Autowired
Publisher<MyResponse> responsePublisher;
@RequestMapping("/mypath")
public SseEmitter index() throws IOException {
SseEmitter emitter = new SseEmitter();
Observable<MyResponse> responseObservable = RxReactiveStreams.toObservable(responsePublisher);
responseObservable.subscribe(
response -> {
try {
emitter.send(response);
} catch (IOException ex) {
emitter.completeWithError(ex);
}
},
error -> {
emitter.completeWithError(error);
},
emitter::complete
);
return emitter;
}
測試類:
//A threaded dummy publisher to demonstrate async properties.
//Sends 2 responses with a 250ms pause in between.
protected static class MockPublisher implements Publisher<MyResponse> {
@Override
public void subscribe(Subscriber<? super MyResponse> subscriber) {
new Thread() {
@Override
public void run() {
try {
subscriber.onNext(response1);
Thread.sleep(250);
subscriber.onNext(response2);
} catch (InterruptedException ex) {
}
subscriber.onComplete();
}
}.start();
}
}
//Assume @Configuration that autowires the above mock publisher in the controller.
//Tests the output of the controller method.
@Test
public void testSseEmitter() throws Exception {
String path = "http://localhost/mypath/";
String expectedContent = "data:" + response1.toString() + "\n\n" +
"data:" + response2.toString() + "\n\n");
//Trial-and-Error attempts at testing this SseEmitter mechanism have yielded the following:
//- Returning an SseEmitter triggers 'asyncStarted'
//- Calling 'asyncResult' forces the test to wait for the process to complete
//- However, there is no actual 'asyncResult' to test. Instead, the content is checked for the published data.
mockMvc.perform(get(path).contentType(MediaType.ALL))
.andExpect(status().isOk())
.andExpect(request().asyncStarted())
.andExpect(request().asyncResult(nullValue()))
.andExpect(header().string("Content-Type", "text/event-stream"))
.andExpect(content().string(expectedContent))
}
如評論中所述,調用 asyncResult() 以確保發布者完成其工作並在測試完成之前發送兩個響應。 沒有它,內容檢查將失敗,因為內容中只存在一個響應。 但是沒有要檢查的實際結果,因此 asyncResult 為空。
我的具體問題是是否有更好、更精確的方法來強制測試等待異步進程完成,而不是等待不存在的 asyncResult 的 klugie 方法。 我更廣泛的問題是,是否有其他庫或 Spring 方法更適合於此與這些異步函數。 謝謝!
這是一個更通用的答案,因為它旨在測試將永遠運行但在給定超時后將與 SSE 流斷開連接的 SseEmitter。
至於與 MVC 不同的方法,正如@ErinDrummond 對 OP 所評論的那樣,您可能想要調查 WebFlux。
這是一個最小的例子。 人們可能想要擴展請求的標頭、不同的匹配器或單獨處理流輸出。
它正在設置一個延遲線程以斷開與 SSE Stream 的連接,這將允許執行斷言。
@Autowired
MockMvc mockMvc;
@Test
public void testSseEmitter(){
ScheduledExecutorService execService = Executors.newScheduledThreadPool(1);
String streamUri = "/your-get-uri");
long timeout = 500L;
TimeUnit timeUnit = TimeUnit.MILLISECONDS;
MvcResult result = mockMvc.perform(get(streamURI)
.andExpect(request().asyncStarted()).andReturn();
MockAsyncContext asyncContext = (MockAsyncContext) result.getRequest().getAsyncContext();
execService.schedule(() -> {
for (AsyncListener listener : asyncContext.getListeners())
try {
listener.onTimeout(null);
} catch (IOException e) {
e.printStackTrace();
}
}, timeout, timeUnit);
result.getAsyncResult();
// assertions, e.g. response body as string contains "xyz"
mvc.perform(asyncDispatch(result)).andExpect(content().string(containsString("xyz")));
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.