All Downloads are FREE. Search and download functionalities are using the official Maven repository.

reactor.test.StepVerifier Maven / Gradle / Ivy

There is a newer version: 3.7.0
Show newest version
/*
 * Copyright (c) 2017-2022 VMware Inc. or its affiliates, All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package reactor.test;

import java.time.Duration;
import java.util.Collection;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.Supplier;


import org.reactivestreams.Publisher;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import reactor.core.Fuseable;
import reactor.core.publisher.Hooks;
import reactor.core.scheduler.Schedulers;
import reactor.test.scheduler.VirtualTimeScheduler;
import reactor.util.annotation.Nullable;
import reactor.util.context.Context;
import reactor.util.function.Tuple2;

/**
 * A {@link StepVerifier} provides a declarative way of creating a verifiable script for
 * an async {@link Publisher} sequence, by expressing expectations about the events that
 * will happen upon subscription. The verification must be triggered after the terminal
 * expectations (completion, error, cancellation) have been declared, by calling one of
 * the {@link #verify()} methods.
 *
 *
 * 
  • Create a {@code StepVerifier} around a {@link Publisher} using * {@link #create create(Publisher)} or * {@link #withVirtualTime withVirtualTime(Supplier<Publisher>)} * (in which case you should lazily create the publisher inside the provided * {@link Supplier lambda}).
  • *
  • Set up individual value expectations using * {@link Step#expectNext expectNext}, * {@link Step#expectNextMatches(Predicate) expectNextMatches(Predicate)}, * {@link Step#assertNext(Consumer) assertNext(Consumer)}, * {@link Step#expectNextCount(long) expectNextCount(long)} or * {@link Step#expectNextSequence(Iterable) expectNextSequence(Iterable)}.
  • *
  • Trigger subscription actions during the verification using either * {@link Step#thenRequest(long) thenRequest(long)} or {@link Step#thenCancel() thenCancel()}.
  • *
  • Finalize the test scenario using a terminal expectation: * {@link LastStep#expectComplete expectComplete()}, * {@link LastStep#expectError expectError()}, * {@link LastStep#expectError(Class) expectError(Class)}, * {@link LastStep#expectErrorMatches(Predicate) expectErrorMatches(Predicate)}, or * {@link LastStep#thenCancel thenCancel()}.
  • *
  • Trigger the verification of the resulting {@code StepVerifier} on its {@code Publisher} * using either {@link #verify()} or {@link #verify(Duration)}. (note some of the terminal * expectations above have a "verify" prefixed alternative that both declare the * expectation and trigger the verification).
  • *
  • If any expectations failed, an {@link AssertionError} will be thrown indicating the * failures.
  • *
* *

For example: *

 * StepVerifier.create(Flux.just("foo", "bar"))
 *   .expectNext("foo")
 *   .expectNext("bar")
 *   .expectComplete()
 *   .verify();
 * 
* * @author Arjen Poutsma * @author Stephane Maldini * @author Simon Baslé */ public interface StepVerifier { /** * Default verification timeout (see {@link #verify()}) is "no timeout". * * @see #setDefaultTimeout(Duration) * @see #resetDefaultTimeout() */ Duration DEFAULT_VERIFY_TIMEOUT = Duration.ZERO; /** * Set the {@link #verify()} timeout for all {@link StepVerifier} created through the * factory methods ({@link #create(Publisher)}, {@link #withVirtualTime(Supplier)}, etc.). *

* This affects ALL such verifiers created after this call, until a call to either * this method or {@link #resetDefaultTimeout()}. * * @param timeout the timeout to use for {@link #verify()} calls on all {@link StepVerifier} * created through the factory methods after this call. {@literal null} is interpreted * as a call to {@link #resetDefaultTimeout()}. */ static void setDefaultTimeout(@Nullable Duration timeout) { DefaultStepVerifierBuilder.defaultVerifyTimeout = timeout == null ? DEFAULT_VERIFY_TIMEOUT : timeout; } /** * Reset the {@link #verify()} timeout to the "unlimited" default. *

* This affects ALL such verifiers created after this call, until a call to * {@link #setDefaultTimeout(Duration)}. */ static void resetDefaultTimeout() { setDefaultTimeout(null); } /** * Prepare a new {@code StepVerifier} in an uncontrolled environment: * {@link Step#thenAwait} will block in real time. * Each {@link #verify()} will fully (re)play the scenario. * * @param publisher the publisher to subscribe to and verify * * @return a builder for expectation declaration and ultimately verification. */ static FirstStep create(Publisher publisher) { return create(publisher, Long.MAX_VALUE); } /** * Prepare a new {@code StepVerifier} in an uncontrolled environment: * {@link Step#thenAwait} will block in real time. * Each {@link #verify()} will fully (re)play the scenario. * The verification will request a specified amount of values. * * @param publisher the publisher to subscribe to and verify * @param n the amount of items to request * * @return a builder for expectation declaration and ultimately verification. */ static FirstStep create(Publisher publisher, long n) { return create(publisher, StepVerifierOptions.create().initialRequest(n)); } /** * Prepare a new {@code StepVerifier} in an uncontrolled environment: * {@link Step#thenAwait} will block in real time. * Each {@link #verify()} will fully (re)play the scenario. * The verification will request a specified amount of values according to * the {@link StepVerifierOptions options} passed. * * @param publisher the publisher to subscribe to * @param options the options for the verification * * @return a builder for expectation declaration and ultimately verification. */ static FirstStep create(Publisher publisher, StepVerifierOptions options) { return DefaultStepVerifierBuilder.newVerifier(options, () -> publisher); } /** * Prepare a new {@code StepVerifier} in a controlled environment using * {@link VirtualTimeScheduler} to manipulate a virtual clock via * {@link Step#thenAwait}. The scheduler is injected into all {@link Schedulers} factories, * which means that any operator created within the lambda without a specific scheduler * will use virtual time. * Each {@link #verify()} will fully (re)play the scenario. * The verification will request an unbounded amount of values. *

* Note that virtual time, {@link Step#thenAwait(Duration)} sources that are * subscribed on a different {@link reactor.core.scheduler.Scheduler} (eg. a source * that is initialized outside of the lambda with a dedicated Scheduler) and * delays introduced within the data path (eg. an interval in a flatMap) are not * always compatible, as this can perform the clock move BEFORE the interval schedules * itself, resulting in the interval never playing out. * * @param scenarioSupplier a mandatory supplier of the {@link Publisher} to subscribe * to and verify. In order for operators to use virtual time, they must be invoked * from within the lambda. * @param the type of the subscriber * * @return a builder for expectation declaration and ultimately verification. */ static FirstStep withVirtualTime(Supplier> scenarioSupplier) { return withVirtualTime(scenarioSupplier, Long.MAX_VALUE); } /** * Prepare a new {@code StepVerifier} in a controlled environment using * {@link VirtualTimeScheduler} to manipulate a virtual clock via * {@link Step#thenAwait}. The scheduler is injected into all {@link Schedulers} factories, * which means that any operator created within the lambda without a specific scheduler * will use virtual time. * Each {@link #verify()} will fully (re)play the scenario. * The verification will request a specified amount of values. *

* Note that virtual time, {@link Step#thenAwait(Duration)} sources that are * subscribed on a different {@link reactor.core.scheduler.Scheduler} (eg. a source * that is initialized outside of the lambda with a dedicated Scheduler) and * delays introduced within the data path (eg. an interval in a flatMap) are not * always compatible, as this can perform the clock move BEFORE the interval schedules * itself, resulting in the interval never playing out. * * @param scenarioSupplier a mandatory supplier of the {@link Publisher} to subscribe * to and verify. In order for operators to use virtual time, they must be invoked * from within the lambda. * @param n the amount of items to request (must be >= 0) * @param the type of the subscriber * * @return a builder for expectation declaration and ultimately verification. */ static FirstStep withVirtualTime(Supplier> scenarioSupplier, long n) { return withVirtualTime(scenarioSupplier, () -> VirtualTimeScheduler.getOrSet(true), n); } /** * Prepare a new {@code StepVerifier} in a controlled environment using * a user-provided {@link VirtualTimeScheduler} to manipulate a virtual clock via * {@link Step#thenAwait}. The scheduler is injected into all {@link Schedulers} factories, * which means that any operator created within the lambda without a specific scheduler * will use virtual time. * Each {@link #verify()} will fully (re)play the scenario. * The verification will request a specified amount of values. *

* Note that virtual time, {@link Step#thenAwait(Duration)} sources that are * subscribed on a different {@link reactor.core.scheduler.Scheduler} (eg. a source * that is initialized outside of the lambda with a dedicated Scheduler) and * delays introduced within the data path (eg. an interval in a flatMap) are not * always compatible, as this can perform the clock move BEFORE the interval schedules * itself, resulting in the interval never playing out. * * @param scenarioSupplier a mandatory supplier of the {@link Publisher} to subscribe * to and verify. In order for operators to use virtual time, they must be invoked * from within the lambda. * @param vtsLookup the supplier of the {@link VirtualTimeScheduler} to inject and * manipulate during verification. * @param n the amount of items to request (must be >= 0) * @param the type of the subscriber * * @return a builder for expectation declaration and ultimately verification. */ static FirstStep withVirtualTime( Supplier> scenarioSupplier, Supplier vtsLookup, long n) { return withVirtualTime(scenarioSupplier, StepVerifierOptions.create() .initialRequest(n) .virtualTimeSchedulerSupplier(vtsLookup)); } /** * Prepare a new {@code StepVerifier} in a controlled environment using * a user-provided {@link VirtualTimeScheduler} to manipulate a virtual clock via * {@link Step#thenAwait}. The scheduler is injected into all {@link Schedulers} factories, * which means that any operator created within the lambda without a specific scheduler * will use virtual time. * Each {@link #verify()} will fully (re)play the scenario. * The verification will request a specified amount of values according to * the provided {@link StepVerifierOptions options}. *

* If no {@link VirtualTimeScheduler} {@link Supplier} is set in the options, this * method will make a {@link StepVerifierOptions#copy() copy} of said options and * set up the default supplier (like the one in {@link #withVirtualTime(Supplier)}). *

* Note that virtual time, {@link Step#thenAwait(Duration)} sources that are * subscribed on a different {@link reactor.core.scheduler.Scheduler} (eg. a source * that is initialized outside of the lambda with a dedicated Scheduler) and * delays introduced within the data path (eg. an interval in a flatMap) are not * always compatible, as this can perform the clock move BEFORE the interval schedules * itself, resulting in the interval never playing out. * * @param scenarioSupplier a mandatory supplier of the {@link Publisher} to subscribe * to and verify. In order for operators to use virtual time, they must be invoked * from within the lambda. * @param options the verification options, including the supplier of the * {@link VirtualTimeScheduler} to inject and manipulate during verification * (see note above in case options doesn't define such a supplier) * @param the type of the subscriber * * @return a builder for expectation declaration and ultimately verification. */ static FirstStep withVirtualTime( Supplier> scenarioSupplier, StepVerifierOptions options) { DefaultStepVerifierBuilder.checkPositive(options.getInitialRequest()); Objects.requireNonNull(scenarioSupplier, "scenarioSupplier"); //force the default VTS supplier if the provided options doesn't define a VTS supplier. //note we make a copy just in case the original options are reused. if (options.getVirtualTimeSchedulerSupplier() == null) { options = options .copy() .virtualTimeSchedulerSupplier(() -> VirtualTimeScheduler.getOrSet(true)); } return DefaultStepVerifierBuilder.newVerifier(options, scenarioSupplier); } /** * Activate debug logging of a description of the test scenario, as well as * some details about certain verification steps. * * @return the verifier for final {@link #verify()} call */ StepVerifier log(); /** * Trigger the subscription and prepare for verifications but doesn't block. Calling one * of the {@link #verify()} methods afterwards will block until the sequence is validated * and throw if assertions fail. *

* Calling this method more than once in a row should be a NO-OP, returning the same * instance as the first call. * * @return a {@link StepVerifier} that is in progress but on which one can chose to block later. */ StepVerifier verifyLater(); /** * Verify the signals received by this subscriber. Unless a default timeout has been * set before construction of the {@link StepVerifier} via {@link StepVerifier#setDefaultTimeout(Duration)}, * this method will block until the stream has been terminated * (either through {@link Subscriber#onComplete()}, {@link Subscriber#onError(Throwable)} or * {@link Subscription#cancel()}). Depending on the declared expectations and actions, * notably in case of undersized manual requests, such a verification could also block * indefinitely. * * @return the actual {@link Duration} the verification took. * @throws AssertionError in case of expectation failures * @see #verify(Duration) * @see #setDefaultTimeout(Duration) */ Duration verify() throws AssertionError; /** * Verify the signals received by this subscriber. This method will * block for up to the given duration or until the stream has been * terminated (either through {@link Subscriber#onComplete()}, * {@link Subscriber#onError(Throwable)} or {@link Subscription#cancel()}). Use * {@link Duration#ZERO} for an unlimited wait for termination. * * @param duration the maximum duration to wait for the sequence to terminate, or * {@link Duration#ZERO} for unlimited wait. * @return the actual {@link Duration} the verification took. * @throws AssertionError in case of expectation failures, or when the verification * times out */ Duration verify(Duration duration) throws AssertionError; /** * {@link #verify() Verifies} the signals received by this subscriber, then exposes * various {@link Assertions assertion methods} on the final state. *

* Note that like {@link #verify()}, this method will block until * the stream has been terminated (either through {@link Subscriber#onComplete()}, * {@link Subscriber#onError(Throwable)} or {@link Subscription#cancel()}). * Depending on the declared expectations and actions, notably in case of undersized * manual requests, such a verification could also block indefinitely. Use * {@link #setDefaultTimeout(Duration)} to globally add a timeout on verify()-derived * methods. * * @return the actual {@link Duration} the verification took. * @throws AssertionError in case of expectation failures */ Assertions verifyThenAssertThat(); /** * {@link #verify() Verifies} the signals received by this subscriber, then exposes * various {@link Assertions assertion methods} on the final state. *

* Note that like {@link #verify()}, this method will block until * the stream has been terminated (either through {@link Subscriber#onComplete()}, * {@link Subscriber#onError(Throwable)} or {@link Subscription#cancel()}). * Depending on the declared expectations and actions, notably in case of undersized * manual requests, such a verification could also block indefinitely. As a consequence * you can use the {@link Duration} {@code duration} parameter to set a timeout. * * @param duration the maximum duration to wait for the sequence to terminate, or * {@link Duration#ZERO} for unlimited wait. * @return {@link Assertions} for chaining post-verification state assertions */ Assertions verifyThenAssertThat(Duration duration); /** * Define a builder for terminal states. */ interface LastStep { /** * Expect an error and consume with the given consumer. Any * {@code AssertionError}s thrown by the consumer will be rethrown * during {@linkplain #verify() verification}. * * @param consumer the consumer for the exception * * @return the built verification */ StepVerifier consumeErrorWith(Consumer consumer); /** * Expect an unspecified error. * * @return the built verification scenario, ready to be verified * * @see Subscriber#onError(Throwable) */ StepVerifier expectError(); /** * Expect an error of the specified type. * * @param clazz the expected error type * * @return the built verification scenario, ready to be verified * * @see Subscriber#onError(Throwable) */ StepVerifier expectError(Class clazz); /** * Expect an error with the specified message. * * @param errorMessage the expected error message * * @return the built verification scenario, ready to be verified * * @see Subscriber#onError(Throwable) */ StepVerifier expectErrorMessage(String errorMessage); /** * Expect an error and evaluate with the given predicate. * * @param predicate the predicate to test on the next received error * * @return the built verification scenario, ready to be verified * * @see Subscriber#onError(Throwable) */ StepVerifier expectErrorMatches(Predicate predicate); /** * Expect an error and assert it via assertion(s) provided as a {@link Consumer}. * Any {@link AssertionError} thrown by the consumer has its message propagated * through a new StepVerifier-specific AssertionError. * * @param assertionConsumer the consumer that applies assertion(s) on the next received error * * @return the built verification scenario, ready to be verified * * @see Subscriber#onError(Throwable) */ StepVerifier expectErrorSatisfies(Consumer assertionConsumer); /** * Verify that the {@link Publisher} under test doesn't terminate but * rather times out after the provided {@link Duration} (a timeout implying * a cancellation of the source). *

* This is equivalent to appending the {@link reactor.core.publisher.Flux#timeout(Duration) timeout} * operator to the publisher and expecting a {@link java.util.concurrent.TimeoutException} * {@link #expectError(Class) onError signal}, while also triggering a {@link Step#thenAwait(Duration) wait} * to ensure unexpected signals are detected. * * @param duration the {@link Duration} for which no new event is expected * @return the built verification scenario, ready to be verified */ StepVerifier expectTimeout(Duration duration); /** * Expect the completion signal. * * @return the built verification scenario, ready to be verified * * @see Subscriber#onComplete() */ StepVerifier expectComplete(); /** * Cancel the underlying subscription. This happens sequentially after the * previous step. *

* Note that time-manipulating operators like {@link Step#expectNoEvent(Duration)} * are detected and waited for before cancellation occurs. * * @return the built verification scenario, ready to be verified * * @see Subscription#cancel() */ StepVerifier thenCancel(); /** * Trigger the {@link #verify() verification}, expecting an unspecified error * as terminal event. *

* This is a convenience method that calls {@link #verify()} in addition to the * expectation. Explicitly use the expect method and verification method * separately if you need something more specific (like activating logging or * changing the default timeout). * * @return the actual {@link Duration} the verification took. * * @see #expectError() * @see #verify() * @see Subscriber#onError(Throwable) */ Duration verifyError(); /** * Trigger the {@link #verify() verification}, expecting an error of the specified * type as terminal event. *

* This is a convenience method that calls {@link #verify()} in addition to the * expectation. Explicitly use the expect method and verification method * separately if you need something more specific (like activating logging or * changing the default timeout). * * @param clazz the expected error type * @return the actual {@link Duration} the verification took. * * @see #expectError(Class) * @see #verify() * @see Subscriber#onError(Throwable) */ Duration verifyError(Class clazz); /** * Trigger the {@link #verify() verification}, expecting an error with the * specified message as terminal event. *

* This is a convenience method that calls {@link #verify()} in addition to the * expectation. Explicitly use the expect method and verification method * separately if you need something more specific (like activating logging or * changing the default timeout). * * @param errorMessage the expected error message * @return the actual {@link Duration} the verification took. * * @see #expectErrorMessage(String) * @see #verify() * @see Subscriber#onError(Throwable) */ Duration verifyErrorMessage(String errorMessage); /** * Trigger the {@link #verify() verification}, expecting an error that matches * the given predicate as terminal event. *

* This is a convenience method that calls {@link #verify()} in addition to the * expectation. Explicitly use the expect method and verification method * separately if you need something more specific (like activating logging or * changing the default timeout). * * @param predicate the predicate to test on the next received error * @return the actual {@link Duration} the verification took. * * @see #expectErrorMatches(Predicate) * @see #verify() * @see Subscriber#onError(Throwable) */ Duration verifyErrorMatches(Predicate predicate); /** * Trigger the {@link #verify() verification}, expecting that the {@link Publisher} * under test doesn't terminate but rather times out after the provided {@link Duration}. * A timeout implies a cancellation of the source. *

* This is a convenience method that calls {@link #verify()} in addition to {@link #expectTimeout(Duration)}. * The later is equivalent to appending the {@link reactor.core.publisher.Flux#timeout(Duration) timeout} * operator to the publisher and expecting a {@link java.util.concurrent.TimeoutException} * {@link #verifyError(Class) onError signal}, while also triggering a {@link Step#thenAwait(Duration) wait} * to ensure unexpected signals are detected. * * @param duration the {@link Duration} for which no new event is expected * @return the actual {@link Duration} the verification took. * * @see #expectTimeout(Duration) * @see #verify() */ default Duration verifyTimeout(Duration duration) { return expectTimeout(duration).verify(); } /** * Trigger the {@link #verify() verification}, expecting an error as terminal event * which gets asserted via assertion(s) provided as a {@link Consumer}. * Any {@link AssertionError} thrown by the consumer has its message propagated * through a new StepVerifier-specific AssertionError. *

* This is a convenience method that calls {@link #verify()} in addition to the * expectation. Explicitly use the expect method and verification method * separately if you need something more specific (like activating logging or * changing the default timeout). * * @param assertionConsumer the consumer that applies assertion(s) on the next received error * * @return the actual {@link Duration} the verification took. * * @see #expectErrorSatisfies(Consumer) * @see #verify() * @see Subscriber#onError(Throwable) */ Duration verifyErrorSatisfies(Consumer assertionConsumer); /** * Trigger the {@link #verify() verification}, expecting a completion signal * as terminal event. *

* This is a convenience method that calls {@link #verify()} in addition to the * expectation. Explicitly use the expect method and verification method * separately if you need something more specific (like activating logging or * changing the default timeout). * * @return the actual {@link Duration} the verification took. * * @see #expectComplete() * @see #verify() * @see Subscriber#onComplete() * */ Duration verifyComplete(); } /** * Define a builder for expecting main sequence individual signals. * * @param the type of values that the subscriber contains */ interface Step extends LastStep { /** * Set a description for the previous verification step. Choosing * a unique and descriptive name can make assertion errors easier to * resolve. *

* Note that calling this several times in a row will only take the * first description into account. * * @param description the description for the previous verification step * @return this builder */ Step as(String description); /** * Expect an element and consume with the given consumer.Any {@code * AssertionError}s thrown by the consumer will be rethrown during {@linkplain * #verify() verification}. * * @param consumer the consumer for the value * * @return this builder */ Step consumeNextWith(Consumer consumer); /** * Expect an element and consume it with the given consumer, usually performing * assertions on it (eg. using Hamcrest, AssertJ or JUnit assertion methods). * Alias for {@link #consumeNextWith(Consumer)} for better discoverability of * that use case. *

* Any {@code AssertionError}s thrown by the consumer will be rethrown during * {@linkplain #verify() verification}. * * @param assertionConsumer the consumer for the value, performing assertions * @return this builder */ default Step assertNext(Consumer assertionConsumer) { return consumeNextWith(assertionConsumer); } /** * Expect a recording session started via {@link #recordWith}, end it and verify * it by applying the given consumer. * Any {@code AssertionError}s thrown by the consumer will be rethrown during * {@linkplain #verify() verification}. * * @param consumer the consumer used to apply assertions on the recorded session * * @return this builder */ Step consumeRecordedWith(Consumer> consumer); /** * Expect the next element received to be equal to the given value. * * @param t the value to expect * * @return this builder * * @see Subscriber#onNext(Object) */ Step expectNext(T t); /** * Expect the next elements received to be equal to the given values. * * @param t1 the first value to expect * @param t2 the second value to expect * * @return this builder * * @see Subscriber#onNext(Object) */ Step expectNext(T t1, T t2); /** * Expect the next elements received to be equal to the given values. * * @param t1 the first value to expect * @param t2 the second value to expect * @param t3 the third value to expect * * @return this builder * * @see Subscriber#onNext(Object) */ Step expectNext(T t1, T t2, T t3); /** * Expect the next elements received to be equal to the given values. * * @param t1 the first value to expect * @param t2 the second value to expect * @param t3 the third value to expect * @param t4 the fourth value to expect * * @return this builder * * @see Subscriber#onNext(Object) */ Step expectNext(T t1, T t2, T t3, T t4); /** * Expect the next elements received to be equal to the given values. * * @param t1 the first value to expect * @param t2 the second value to expect * @param t3 the third value to expect * @param t4 the fourth value to expect * @param t5 the fifth value to expect * * @return this builder * * @see Subscriber#onNext(Object) */ Step expectNext(T t1, T t2, T t3, T t4, T t5); /** * Expect the next elements received to be equal to the given values. * * @param t1 the first value to expect * @param t2 the second value to expect * @param t3 the third value to expect * @param t4 the fourth value to expect * @param t5 the fifth value to expect * @param t6 the sixth value to expect * * @return this builder * * @see Subscriber#onNext(Object) */ Step expectNext(T t1, T t2, T t3, T t4, T t5, T t6); /** * Expect the next elements received to be equal to the given values. * * @param ts the values to expect * * @return this builder * * @see Subscriber#onNext(Object) */ @SuppressWarnings("unchecked") Step expectNext(T... ts); /** * Expect to received {@code count} elements, starting from the previous * expectation or onSubscribe. * * @param count the number of emitted items to expect. * * @return this builder * * @see Subscriber#onNext(Object) */ Step expectNextCount(long count); /** * Expect the next elements to match the given {@link Iterable} until its * iterator depletes. * * @param iterable the iterable containing the next expected values * * @return this builder * * @see Subscriber#onNext(Object) */ Step expectNextSequence(Iterable iterable); /** * Expect an element and evaluate with the given predicate. * * @param predicate the predicate to test on the next received value * * @return this builder * * @see Subscriber#onNext(Object) */ Step expectNextMatches(Predicate predicate); /** * Expect a {@link Subscription} and consume with the given consumer. Any {@code * AssertionError}s thrown by the consumer will be rethrown during {@linkplain * #verify() verification}. * * @param consumer the consumer for the {@link Subscription} * * @return this builder * * @see Subscriber#onSubscribe(Subscription) */ Step consumeSubscriptionWith(Consumer consumer); /** * Expect that after the {@link Subscription} step, a {@link Context} has been * propagated. You can continue with assertions on said {@link Context}, and you * will need to use {@link ContextExpectations#then()} to switch back to verifying * the sequence itself. * * @return a {@link ContextExpectations} for further assertion of the propagated {@link Context} */ ContextExpectations expectAccessibleContext(); /** * Expect that NO {@link Context} was propagated after the {@link Subscription} * phase, which usually indicates that the sequence under test doesn't contain * Reactor operators (i.e. external Publisher, just a scalar source...). * * @return this builder for further assertion of the sequence */ Step expectNoAccessibleContext(); /** * Expect that no event has been observed by the verifier for the length of * the provided {@link Duration}. If virtual time is used, this duration is * verified using the virtual clock. *

* Note that you should only use this method as the first expectation if you * actually don't expect a subscription to happen. Use * {@link FirstStep#expectSubscription()} combined with {@link Step#expectNoEvent(Duration)} * to work around that. *

* Also avoid using this method at the end of the set of expectations: * prefer {@link #expectTimeout(Duration)} rather than {@code expectNoEvent(...).thenCancel()}. * * @param duration the duration for which to observe no event has been received * * @return this builder * * @see Subscriber */ Step expectNoEvent(Duration duration); /** * Expect a recording session started via {@link #recordWith}, end it and verify * it by ensuring the provided predicate matches. * * @param predicate the predicate to test on the recorded session * * @return this builder * * @see Subscriber#onNext(Object) */ Step expectRecordedMatches(Predicate> predicate); /** * Start a recording session storing {@link Subscriber#onNext(Object)} values in * the supplied {@link Collection}. Further steps * {@link #expectRecordedMatches(Predicate)} and * {@link #consumeRecordedWith(Consumer)} can consume and assert the session. *

If an existing recording session hasn't not been declaratively consumed, this step * will override the current session. * * @param supplier the supplier for the {@link Collection} to use for recording. * * @return this builder */ Step recordWith(Supplier> supplier); /** * Run an arbitrary task scheduled after previous expectations or tasks. * * @param task the task to run * * @return this builder */ Step then(Runnable task); /** * Mark a Pause in the expectation evaluation. * If a {@link VirtualTimeScheduler} has been configured, * {@link VirtualTimeScheduler#advanceTime()} will be used and the * pause will not block testing or {@link Publisher} thread. * * @return this builder */ default Step thenAwait() { return thenAwait(Duration.ZERO); } /** * Pause the expectation evaluation for a given {@link Duration}. * If a {@link VirtualTimeScheduler} has been configured, * {@link VirtualTimeScheduler#advanceTimeBy(Duration)} will be used and the * pause will not block testing or {@link Publisher} thread. * * @param timeshift a pause {@link Duration} * * @return this builder */ Step thenAwait(Duration timeshift); /** * Consume further onNext signals as long as they match a predicate. * * @param predicate the condition to continue consuming onNext * * @return this builder */ Step thenConsumeWhile(Predicate predicate); /** * Consume further onNext signals using a provided {@link Consumer} as long as * they match a {@link Predicate}. You can use the consumer to apply assertions * on each value. * * @param predicate the condition to continue consuming onNext * @param consumer the consumer to use to consume the data, when the predicate * matches * * @return this builder */ Step thenConsumeWhile(Predicate predicate, Consumer consumer); /** * Request the given amount of elements from the upstream {@code Publisher}. This * is in addition to the initial number of elements requested by an * initial passed demand like with {@link StepVerifier#create(Publisher, long)}. * * @param n the number of elements to request * * @return this builder * * @see Subscription#request(long) */ Step thenRequest(long n); } /** * Define a builder for explicitly expecting an initializing {@link Subscription} as * first signal. *

* If {@link FirstStep} expectations are not used, the produced * {@link StepVerifier} keeps a first expectation that will be checking if * the first signal is a * {@link Subscription}. * * @param the type of values that the subscriber contains */ interface FirstStep extends Step { /** * Provide a {@link Predicate} that will turn this StepVerifier's subscribers into * {@link reactor.core.Fuseable.ConditionalSubscriber} and drive the {@link reactor.core.Fuseable.ConditionalSubscriber#tryOnNext(Object)} * behavior of these subscribers. Set to {@code null} to deactivate (the default). * * @param tryOnNextPredicate the {@link Predicate} to use for conditional tryOnNext path * @return the verifier for final {@link #verify()} call */ default FirstStep enableConditionalSupport(@Nullable Predicate tryOnNextPredicate) { if (tryOnNextPredicate != null) { throw new UnsupportedOperationException("This implementation of StepVerifier doesn't support ConditionalSubscriber mode"); } return this; } /** * Expect the source {@link Publisher} to run with Reactor Fusion flow * optimization. It will be requesting {@link Fuseable#ANY} fusion mode. * * @return this builder * * @see Fuseable */ Step expectFusion(); /** * Expect the source {@link Publisher} to run the requested Reactor Fusion mode * from any of these modes : * {@link Fuseable#NONE}, {@link Fuseable#SYNC}, {@link Fuseable#ASYNC}, * {@link Fuseable#ANY}, {@link Fuseable#THREAD_BARRIER}. * * @param requested the requested and expected fusion mode * * @return this builder * * @see Fuseable */ Step expectFusion(int requested); /** * Expect the source {@link Publisher} to run with Reactor Fusion flow * optimization. * Expect the source {@link Publisher} to run the requested Reactor Fusion mode * from any of these modes : * {@link Fuseable#NONE}, {@link Fuseable#SYNC}, {@link Fuseable#ASYNC}, * {@link Fuseable#ANY}, {@link Fuseable#THREAD_BARRIER}. * * @param requested the requested fusion mode * @param expected the expected fusion mode * * @return this builder * * @see Fuseable */ Step expectFusion(int requested, int expected); /** * Expect the source {@link Publisher} to NOT run with Reactor Fusion flow * optimization. It will check if publisher is {@link Fuseable} or * subscription is a {@link reactor.core.Fuseable.QueueSubscription}. * * @return this builder * * @see Fuseable */ Step expectNoFusionSupport(); /** * Expect a {@link Subscription}. * Effectively behave as the default implicit {@link Subscription} expectation. * * @return this builder * * @see Subscriber#onSubscribe(Subscription) */ Step expectSubscription(); /** * Expect a {@link Subscription} and evaluate with the given predicate. * * @param predicate the predicate to test on the received {@link Subscription} * * @return this builder * * @see Subscriber#onSubscribe(Subscription) */ Step expectSubscriptionMatches(Predicate predicate); } /** * Exposes post-verification state assertions. */ interface Assertions { /** * Assert that the tested publisher has dropped at least one element to the * {@link Hooks#onNextDropped(Consumer)} hook. */ Assertions hasDroppedElements(); /** * Assert that the tested publisher has not dropped any element to the * {@link Hooks#onNextDropped(Consumer)} hook. */ Assertions hasNotDroppedElements(); /** * Assert that the tested publisher has dropped at least all of the provided * elements to the {@link Hooks#onNextDropped(Consumer)} hook, in any order. */ Assertions hasDropped(Object... values); /** * Assert that the tested publisher has dropped all of the provided elements to * the {@link Hooks#onNextDropped(Consumer)} hook, in any order, and that no * other elements were dropped. */ Assertions hasDroppedExactly(Object... values); /** * Assert that the tested publisher has discarded at least one element to the * {@link reactor.core.publisher.Flux#doOnDiscard(Class, Consumer) discard} hook. *

* Unlike {@link #hasDroppedElements()}, the discard hook can be invoked as part * of normal operations, eg. when an element doesn't match a filter. */ Assertions hasDiscardedElements(); /** * Assert that the tested publisher has not discarded any element to the * {@link reactor.core.publisher.Flux#doOnDiscard(Class, Consumer) discard} hook. *

* Unlike {@link #hasDroppedElements()}, the discard hook can be invoked as part * of normal operations, eg. when an element doesn't match a filter. */ Assertions hasNotDiscardedElements(); /** * Assert that the tested publisher has discarded at least all of the provided * elements to the {@link reactor.core.publisher.Flux#doOnDiscard(Class, Consumer) discard} hook, in any order. *

* Unlike {@link #hasDroppedElements()}, the discard hook can be invoked as part * of normal operations, eg. when an element doesn't match a filter. */ Assertions hasDiscarded(Object... values); /** * Assert that the tested publisher has discarded all of the provided elements to * the {@link reactor.core.publisher.Flux#doOnDiscard(Class, Consumer) discard} hook, * in any order, and that no other elements were dropped. *

* Unlike {@link #hasDroppedElements()}, the discard hook can be invoked as part * of normal operations, eg. when an element doesn't match a filter. */ Assertions hasDiscardedExactly(Object... values); /** * Assert that the tested publisher has discarded one or more elements to the * {@link reactor.core.publisher.Flux#doOnDiscard(Class, Consumer) discard} hook, * and check that the collection of discarded elements matches a predicate. */ Assertions hasDiscardedElementsMatching(Predicate> matcher); /** * Assert that the tested publisher has discarded one or more elements to the * {@link reactor.core.publisher.Flux#doOnDiscard(Class, Consumer) discard} hook, and assert them as a collection. */ Assertions hasDiscardedElementsSatisfying(Consumer> consumer); /** * Assert that the tested publisher has dropped at least one error to the * {@link Hooks#onErrorDropped(Consumer)} hook. */ Assertions hasDroppedErrors(); /** * Assert that the tested publisher has not dropped any error to the * {@link Hooks#onErrorDropped(Consumer)} hook. */ Assertions hasNotDroppedErrors(); /** * Assert that the tested publisher has dropped exactly n errors to the * {@link Hooks#onErrorDropped(Consumer)} hook. */ Assertions hasDroppedErrors(int n); /** * Assert that the tested publisher has dropped exactly one error of the given type * to the {@link Hooks#onErrorDropped(Consumer)} hook. */ Assertions hasDroppedErrorOfType(Class clazz); /** * Assert that the tested publisher has dropped exactly one error matching the given * predicate to the {@link Hooks#onErrorDropped(Consumer)} hook. */ Assertions hasDroppedErrorMatching(Predicate matcher); /** * Assert that the tested publisher has dropped exactly one error with the exact provided * message to the {@link Hooks#onErrorDropped(Consumer)} hook. */ Assertions hasDroppedErrorWithMessage(String message); /** * Assert that the tested publisher has dropped exactly one error with a message containing * the provided string to the {@link Hooks#onErrorDropped(Consumer)} hook. */ Assertions hasDroppedErrorWithMessageContaining(String messagePart); /** * Assert that the tested publisher has dropped one or more errors to the * {@link Hooks#onErrorDropped(Consumer)} hook, and assert them as a collection. */ Assertions hasDroppedErrorsSatisfying(Consumer> errorsConsumer); /** * Assert that the tested publisher has dropped one or more errors to the * {@link Hooks#onErrorDropped(Consumer)} hook, and check that the collection of * errors matches a predicate. */ Assertions hasDroppedErrorsMatching(Predicate> errorsConsumer); /** * Assert that the tested publisher has triggered the {@link Hooks#onOperatorError(BiFunction) onOperatorError} hook * at least once. */ Assertions hasOperatorErrors(); /** * Assert that the tested publisher has triggered the {@link Hooks#onOperatorError(BiFunction) onOperatorError} hook * exactly n times. */ Assertions hasOperatorErrors(int n); /** * Assert that the tested publisher has triggered the {@link Hooks#onOperatorError(BiFunction) onOperatorError} hook * exactly once and the error is of the given type. */ Assertions hasOperatorErrorOfType(Class clazz); /** * Assert that the tested publisher has triggered the {@link Hooks#onOperatorError(BiFunction) onOperatorError} hook * exactly once and the error matches the given predicate. */ Assertions hasOperatorErrorMatching(Predicate matcher); /** * Assert that the tested publisher has triggered the {@link Hooks#onOperatorError(BiFunction) onOperatorError} hook * exactly once and the error has the exact provided message. */ Assertions hasOperatorErrorWithMessage(String message); /** * Assert that the tested publisher has triggered the {@link Hooks#onOperatorError(BiFunction) onOperatorError} hook * exactly once, with the error message containing the provided string. */ Assertions hasOperatorErrorWithMessageContaining(String messagePart); /** * Assert that the tested publisher has triggered the {@link Hooks#onOperatorError(BiFunction) onOperatorError} hook * once or more, and assert the errors and optionally associated data as a collection. */ Assertions hasOperatorErrorsSatisfying(Consumer, Optional>>> errorsConsumer); /** * Assert that the tested publisher has triggered the {@link Hooks#onOperatorError(BiFunction) onOperatorError} hook * once or more, and check that the collection of errors and their optionally * associated data matches a predicate. */ Assertions hasOperatorErrorsMatching(Predicate, Optional>>> errorsConsumer); /** * Assert that the whole verification took strictly less than the provided * duration to execute. * @param d the expected maximum duration of the verification */ Assertions tookLessThan(Duration d); /** * Assert that the whole verification took strictly more than the provided * duration to execute. * @param d the expected minimum duration of the verification */ Assertions tookMoreThan(Duration d); } /** * Allow to set expectations about the {@link Context} propagated during the {@link Subscription} * phase. You then need to resume to the {@link StepVerifier} assertion of the sequence * by using {@link #then()}. * * @param the type of the sequence */ interface ContextExpectations { /** * Check that the propagated {@link Context} contains a value for the given key. * * @param key the key to check * @return this {@link ContextExpectations} for further Context checking */ ContextExpectations hasKey(Object key); /** * Check that the propagated {@link Context} is of the given size. * * @param size the expected size * @return this {@link ContextExpectations} for further Context checking */ ContextExpectations hasSize(int size); /** * Check that the propagated {@link Context} contains the given value associated * to the given key. * * @param key the key to check for * @param value the expected value for that key * @return this {@link ContextExpectations} for further Context checking */ ContextExpectations contains(Object key, Object value); /** * Check that the propagated {@link Context} contains all of the key-value pairs * of the given {@link Context}. * * @param other the other {@link Context} whose key-value pairs are expected to be * all contained by the propagated Context. * @return this {@link ContextExpectations} for further Context checking */ ContextExpectations containsAllOf(Context other); /** * Check that the propagated {@link Context} contains all of the key-value pairs * of the given {@link Map}. * * @param other the other {@link Map} whose key-value pairs are expected to be all * contained by the propagated {@link Context}. * @return this {@link ContextExpectations} for further Context checking */ ContextExpectations containsAllOf(Map other); /** * Check that the propagated {@link Context} contains all of the key-value pairs * of the given {@link Context}, and nothing else. * * @param other the {@link Context} representing the exact expected content of the * propagated Context. * @return this {@link ContextExpectations} for further Context checking */ ContextExpectations containsOnly(Context other); /** * Check that the propagated {@link Context} contains all of the key-value pairs * of the given {@link Map}, and nothing else. * * @param other the {@link Map} representing the exact expected content of the * propagated {@link Context}. * @return this {@link ContextExpectations} for further Context checking */ ContextExpectations containsOnly(Map other); /** * Apply custom assertions to the propagated {@link Context}. * * @param assertingConsumer a {@link Consumer} for the {@link Context} that applies * custom assertions. * @return this {@link ContextExpectations} for further Context checking */ ContextExpectations assertThat(Consumer assertingConsumer); /** * Check that the propagated {@link Context} matches a given {@link Predicate}. * * @param predicate a {@link Predicate} to test with the {@link Context}. * @return this {@link ContextExpectations} for further Context checking */ ContextExpectations matches(Predicate predicate); /** * Check that the propagated {@link Context} matches a given {@link Predicate} * that is given a description. * * @param predicate a {@link Predicate} to test with the {@link Context} * @param description the description for the Predicate, which will be part of * the {@link AssertionError} if the predicate fails. * @return this {@link ContextExpectations} for further Context checking */ ContextExpectations matches(Predicate predicate, String description); /** * Add the context checks set up by this {@link ContextExpectations} as a * {@link StepVerifier} expectation, and return the current {@link Step} in order * to build further sequence expectations and ultimately {@link StepVerifier#verify() * verify} the sequence. * * @return the {@link Step} for further building of expectations. */ Step then(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy