#java #spring-boot #unit-testing #spring-webflux #project-reactor
Вопрос:
У меня есть метод, который принимает поток в качестве параметра. Внутри метода он затем настраивает поток, и другой метод подписывается на него.
public class StringProcessor {
private final stringParsingService stringParsingService;
public void subscribeStringFlux(Flux<String> stringFlux) {
fluxConfiguration(stringFlux)
.subscribe();
}
Flux<String> fluxConfiguration(Flux<String> stringFlux) {
return stringFlux
.filter(stringValidatorr::isValidString)
.doOnNext(itemString -> {
List<String> values = stringParsingService.parseValues(itemString);
})
.onErrorContinue((e,object) -> log.error(e.getClass().toString() " " e.getMessage()));
}
}
Я пытаюсь проверить, что код в doOnNext выполнен. Я пытался использовать StepVerifier (с Mockito для службы), однако, кажется, что он никогда не вводит код во время теста, когда я помещаю точки останова в stringParsingService.parseValue(). Тем не менее, код действительно выполняется и выполняется должным образом, хотя и без выполнения в тесте с реальными данными.
Мой вопрос в том, как вы пишете тесты, которые охватывают действия, выполняемые в Flux.doOnNext()? Есть ли способ использовать StepVerifier, который позволит ему выполнять код в doOnNext()? Я искал несколько дней, пробовал несколько подходов, и до сих пор ни один из них не сработал.
Единственный способ, который я нашел до сих пор, который даже близко подходит, — это сделать следующее (однако это, конечно, не учитывается для покрытия кода).:
Flux<String> testStringFlux = Flux.just("a_test_string");
StepVerifier.create(testStringFlux)
.consumeNextWith(itemString -> {
List<String> values = stringParsingService.parseValues(itemString);
})
.verifyComplete();
Комментарии:
1. Вы создаете тестовую строку, вызываете свою функцию с помощью этой тестовой строки. Функция возвращает поток. Вы запускаете этот поток в пошаговом преобразователе, проходите через него и утверждаете результаты.
Ответ №1:
doOnNext
является побочным оператором, что означает, что выполняемая им работа на самом деле не видна с точки зрения основной реактивной последовательности (the Flux<T>
). В результате ни один из инструментов, используемых для тестирования a Flux
, не может действительно увидеть и протестировать побочный эффект, если вы не сделаете его тестируемым явно.
Одним из возможных способов было бы сделать StringProcessor.stringParsingService
используемый в вашем тесте макет или экземпляр для конкретного теста, который записывает проанализированные строки, а затем утверждает это в конце Flux
последовательности.
Обратите внимание , что вы doOnNext
вычисляете a List
, но после этого этот список не используется. Излучаемые элементы someFlux.doOnNext(function)
точно такие же , как и излучаемые someFlux
, независимо от того, что делается внутри function
(если function
не считать бросков, но это уже другая история).
Комментарии:
1. Спасибо вам за ответ. Мне кажется странным, что побочные эффекты не поддаются проверке и являются своего рода слепым пятном в функциональности StepVerifier.
2. это сродни высказыванию «Я хочу проверить эффект выполнения a
ExecutorService.submit(Runnable)
«, если вам нужна точка сравнения с классом JDK.
Ответ №2:
если вы настроите свой поток следующим образом, он остановится в точке останова функции parseValues. Но я не уверен, что это то, о чем вы просите…
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>flux_test</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<version>3.4.8</version>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
<version>3.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
</project>
StringProcessor.java
import org.apache.log4j.Logger;
import reactor.core.publisher.Flux;
import java.util.Arrays;
import java.util.List;
public class StringProcessor {
private final static Logger log = Logger.getLogger(StringProcessor.class.getName());
private class StringParsingService {
List<String> parseValues(String str) {
return Arrays.asList(str.split(","));
}
}
private static class StringValidator {
static boolean isValidString(String str) {
return str.length() > 1;
}
}
private final StringParsingService stringParsingService = new StringParsingService();
public void subscribeStringFlux(Flux<String> stringFlux) {
fluxConfiguration(stringFlux)
.subscribe();
}
Flux<String> fluxConfiguration(Flux<String> stringFlux) {
return stringFlux
.filter(StringValidator::isValidString)
.doOnNext(itemString -> {
List<String> values = stringParsingService.parseValues(itemString);
})
.onErrorContinue((e, object) -> log.error(e.getClass().toString() " " e.getMessage()));
}
}
TestFlux.java
import org.junit.Test;
import reactor.test.StepVerifier;
import reactor.test.publisher.TestPublisher;
public class TestFlux {
final TestPublisher<String> testPublisher = TestPublisher.create();
@Test
public void testFlux() {
StepVerifier.create(new StringProcessor().fluxConfiguration(testPublisher.flux()))
.then(() -> testPublisher.emit("aa,bb", "cc,dd"))
.expectNext("aa,bb")
.expectNext("cc,dd")
.verifyComplete();
}
}
Комментарии:
1. Спасибо, что нашли время ответить,когда я запускаю тест так, как вы написали, просто выдавая первую строку и только один ожидаемый текст(«aa, bb») Я получаю ошибку утверждения «ожидаемое: onNext(«aa,bb») фактическое: onComplete()». Я использую Junit 5, но в остальном моя настройка точно такая же, как у вашего pom (я использую реактор Spring Boot). Кроме того, да, я хочу иметь возможность достичь точки останова в методе parseValues во время тестирования.
2. Если вы хотите выпустить и ожидаете только один элемент, вы можете изменить эмиттер на testPublisher.emit(«aa,bb») , тогда он будет успешным. В обоих случаях вы должны иметь возможность использовать точки останова, если запускаете тесты в режиме отладки…