Я искал подсказки о том, как лучше всего тестировать методы Spring MVC Controller, которые возвращают SseEmitters. Я пришел довольно коротко, но у меня есть решение проб и ошибок, которое тестирует асинхронное многопоточное поведение. Ниже приведен пример кода только для демонстрации концепции, может быть опечатка или две:
Класс контроллера:
@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 имеет значение null.
Мой конкретный вопрос заключается в том, есть ли лучший и более точный способ заставить тест ждать завершения асинхронного процесса, а не метод klugie здесь для ожидания несуществующего asyncResult. Мой более широкий вопрос заключается в том, существуют ли другие библиотеки или методы Spring, которые лучше подходят для этого по сравнению с этими асинхронными функциями. Спасибо!
SseEmitter
, используя обычный Spring MVC вместо Spring WebFlux - так что спасибо! - person Erin Drummond   schedule 05.06.2019