This series of articles index the "Response Spring's word Wizard"
Previously summary: Reactor 3 Quick Start | Responsive Flow Specification
This article tests the source code
2.6 Testing
Automated testing is an important means of ensuring code quality in today's highly focused devops and in a number of TDD-pursuing teams. To perform a reactor test, first make sure to add a reactor-test
dependency.
Reactor-test with Maven configuration <dependencies>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<version>3.1.4.RELEASE</version>
<scope>test</scope>
</dependency>
Reactor-test configuration with Gradle
dependencies {
Testcompile ' Io.projectreactor:reactor-test:3.1.4.release '
}
2.6.1 using Stepverifier to test
1.3.2.3 A preliminary introduction StepVerifier
to the usage. As an example, recall:
@Test public void testAppendBoomError() { Flux<String> source = Flux.just("foo", "bar"); StepVerifier.create( appendBoomError(source)) .expectNext("foo") .expectNext("bar") .expectErrorMessage("boom") .verify(); }
We typically use create
methods to create flux-or mono-based StepVerifier
, and then we can test the following:
- Test the next signal that is expected to be emitted. If another signal is received (or the signal does not match the expectation), the entire test fails (assertionerror), such as
expectNext(T...)
or expectNextCount(long)
. `
- Processing (consume) the next signal. Use it when you want to skip part of the sequence or when you want to make a custom check on the content of the signal
consumeNextWith(Consumer<T>)
.
- Other actions, such as pausing or running a piece of code. For example, if you want to adjust or process the test status or content, you may use the
thenAwait(Duration)
and then(Runnable)
.
For the terminating event, the corresponding expected method (such as, expectComplete()
expectError()
, and all of its variant methods) can no longer be used to add another desired method. Finally, you can only perform some additional configuration on the Stepverifier and trigger the checksum (usually called verify()
and its Variant method).
From an StepVerifier
internal implementation, it subscribes to the Flux or Mono to be tested and then pairs each signal in the sequence against the expectations of the test scenario. If it matches, the test succeeds. If there is a mismatch, an exception is thrown AssertionError
.
A responsive flow is a time-based data flow. Many times, the data stream being tested is delayed and lasts for some time. If this scenario is more, it can lead to a longer automated test run time. StepVerifier
This provides a way to test how virtual time can be manipulated, which needs to be used StepVerifier.withVirtualTime
to construct it.
In order to improve the probability of normal functioning of the stepverifier, it generally does not receive a simple flux as input, but instead receives one Supplier
, which allows the "lazy creation" of flux to be tested after the Subscriber has been configured, such as:
StepVerifier.withVirtualTime(() -> Mono.delay(Duration.ofDays(1)))//... 继续追加期望方法
There are two expected methods of processing time, regardless of whether the configured virtual time is available:
thenAwait(Duration)
The verification step is paused (allows signal delay to be emitted).
expectNoEvent(Duration)
Also let the sequence persist for a certain period of time, during which the test fails if any signal is emitted.
In a normal test, two methods suspend the execution of a thread based on the given duration. In virtual time mode, virtual time is used accordingly.
StepVerifier.withVirtualTime(() -> Mono.delay(Duration.ofDays(1))) .expectSubscription() // 1 .expectNoEvent(Duration.ofDays(1)) // 2 .expectNext(0L) .verifyComplete(); // 3
- Expectnoevent the Subscription (subscription) is also considered an event. Assuming you use it as a first step, you will fail if you detect a subscription signal. This time can be used
expectSubscription().expectNoEvent(duration)
to replace;
- Expect no signal to occur in "one day";
verify
Or the Variant method will eventually return one Duration
, which is the actual duration of the test.
As you can see, Withvirtualtime lets us not actually wait 1 days to complete the test.
How is the function of virtual time implemented? StepVerifier.withVirtualTime
a custom scheduler is inserted in the reactor Scheduler factory method instead of the VirtualTimeScheduler
default scheduler (those time-based operators typically use the scheduler by default Schedulers.parallel()
).
2.6.2 checking the execution path with Publisherprobe
In general, StepVerifier
expect*
Most test scenarios can be done with the use. However, it also has a time to do something, such as this particular example below:
private Mono<String> executeCommand(String command) { // 基于command执行一些操作,执行完成后返回Mono<String> } public Mono<Void> processOrFallback(Mono<String> commandSource, Mono<Void> doWhenEmpty) { return commandSource .flatMap(command -> executeCommand(command).then()) // 1 .switchIfEmpty(doWhenEmpty); // 2 }
then()
All elements are ignored, only the completion signal is preserved, so the return value is mono<void>;
- is also a mono<void>.
1 and 2 are Mono<Void>
, this time it is more difficult to judge processOfFallback
the specific execution of which path. This time can be used log()
or doOn*()
other methods to observe, but this "in the non-green is red" in the single test does not work. Or the value of the identity state is added to a path and determined by judging whether the state value is correct, but this requires modifying the processOfFallback
code being tested.
Reactor version 3.1.0 We can use it PublisherProbe
to do a similar scenario validation. As follows:
@Test public void testWithPublisherProbe() { PublisherProbe<Void> probe = PublisherProbe.empty(); // 1 StepVerifier.create(processOrFallback(Mono.empty(), probe.mono())) // 2 .verifyComplete(); probe.assertWasSubscribed(); // 3 probe.assertWasRequested(); // 4 probe.assertWasNotCancelled(); // 5 }
- Create a probe (probe) that converts to an empty sequence.
- Call Probe.mono () at the location where you want to use mono<void> to replace the probe.
- After the sequence is over, you can use this probe to determine how the sequence is used, and you can check where it was subscribed from (the path) ...?
- The same is true for requests ...?
- And whether it was canceled.
2.6.3 using Testpublisher to emit elements manually
TestPublisher
is essentially one Publisher
, but it can be more "free and unrestrained" to emit a variety of elements, in order to conduct various scenarios of testing.
1) "Free" to emit elements
We can use the method it provides to send out various signals:
next(T)
and next(T, T...)
emit 1-n onNext signals.
emit(T...)
Performs the same function and will execute complete ().
complete()
Will emit a terminating signal oncomplete.
error(Throwable)
Will emit a terminating signal onError.
Like what:
@Test public void testWithTestPublisher() { TestPublisher<Integer> testPublisher = TestPublisher.<Integer>create().emit(1, 2, 3); StepVerifier.create(testPublisher.flux().map(i -> i * i)) .expectNext(1, 4, 9) .expectComplete(); }
2) "unrestrained" to emit elements
Use create
the factory method to get a normal TestPublisher
. Using the createNonCompliant
factory method, you can create an "unhealthy" one TestPublisher
. The latter needs to pass in TestPublisher.Violation
a set of options specified by the enumeration, which can be used to tell publisher which issues to ignore. The enumeration values are:
REQUEST_OVERFLOW
: Allows next to be invoked when the request is insufficient, without triggering illegalstateexception.
ALLOW_NULL
: Allows next to emit a null value without triggering nullpointerexception.
CLEANUP_ON_TERMINATE
: The termination signal can be repeatedly signaled, including complete (), error (), and emit ().
However, this feature may be more used by reactor project developers themselves, such as when they develop a new operator that can be used to test whether the operator satisfies the specification of a responsive stream.
3) Testpublisher is a publisherprobe.
Better TestPublisher
Yet, implementing an PublisherProbe
interface means that we can also use the methods it provides assert*
to keep track of its internal subscription and execution status.
(+) Reactor test--response spring's DAO spell device