(+) Reactor test--response spring's DAO spell device

Source: Internet
Author: User
Tags emit

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&lt;T&gt;) .
    • 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. StepVerifierThis 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
    1. 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;
    2. Expect no signal to occur in "one day";
    3. verifyOr 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.withVirtualTimea 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    }
    1. then()All elements are ignored, only the completion signal is preserved, so the return value is mono<void>;
    2. is also a mono<void>.

1 and 2 are Mono&lt;Void&gt; , 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    }
    1. Create a probe (probe) that converts to an empty sequence.
    2. Call Probe.mono () at the location where you want to use mono<void> to replace the probe.
    3. 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) ...?
    4. The same is true for requests ...?
    5. And whether it was canceled.
2.6.3 using Testpublisher to emit elements manually

TestPublisheris 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

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.