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

reactor.test.StepVerifier Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2011-2016 Pivotal Software Inc, 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
 *
 *       http://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.Objects;
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.test.scheduler.VirtualTimeScheduler;
import reactor.util.function.Tuple2;

/**
 * A {@link StepVerifier} is a verifiable, blocking script usually produced by
 * terminal expectations of the said script.
 * 
  • Create a {@code * StepVerifier} builder using {@link #create} or {@link #withVirtualTime}
  • *
  • Set individual up value expectations using * {@link Step#expectNext}, {@link Step#expectNextMatches(Predicate)}, * {@link Step#expectNextCount(long)} or * {@link Step#expectNextSequence(Iterable)} * .
  • Set up * subscription actions using either * {@link Step#thenRequest(long) thenRequest(long)} or {@link * Step#thenCancel() thenCancel()}.
  • Build the {@code * StepVerifier} using {@link LastStep#expectComplete}, * {@link LastStep#expectError}, {@link * LastStep#expectError(Class) expectError(Class)}, {@link * LastStep#expectErrorMatches(Predicate) expectErrorMatches(Predicate)}, or {@link * LastStep#thenCancel}.
  • Subscribe the built {@code * StepVerifier} to a {@code Publisher}.
  • Verify the expectations using * either {@link #verify()} or {@link #verify(Duration)}.
  • If any expectations * failed, an {@code 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 */ public interface StepVerifier { /** * Prepare a new {@code StepVerifier} in an uncontrolled environment: Expect non-virtual * blocking * wait via * {@link Step#thenAwait}. Each {@link #verify()} will fully (re)play the * scenario. * * @param publisher the publisher to subscribe to * * @return the {@link Duration} of the verification * * @throws AssertionError in case of expectation failures */ static FirstStep create(Publisher publisher) { return create(publisher, Long.MAX_VALUE); } /** * Prepare a new {@code StepVerifier} in an uncontrolled environment: Expect non-virtual * blocking * wait via * {@link Step#thenAwait}. 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 * @param n the amount of items to request * * @return the {@link Duration} of the verification * * @throws AssertionError in case of expectation failures, or when the verification * times out */ static FirstStep create(Publisher publisher, long n) { return create(publisher, StepVerifierOptions.create().initialRequest(n)); } /** * Prepare a new {@code StepVerifier} in an uncontrolled environment: Expect non-virtual * blocking wait via {@link Step#thenAwait}. 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 the {@link Duration} of the verification * * @throws AssertionError in case of expectation failures, or when the verification * times out */ 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 schedule and expect virtual wait via * {@link Step#thenAwait}. Each {@link #verify()} will fully (re)play the * scenario. The verification will request an unbounded amount of values. * * @param scenarioSupplier a mandatory supplier of the {@link Publisher} to test * @param the type of the subscriber * * @return a builder for setting up value expectations */ static FirstStep withVirtualTime(Supplier> scenarioSupplier) { return withVirtualTime(scenarioSupplier, Long.MAX_VALUE); } /** * @deprecated to be removed in 3.1.0 for parameter ordering harmonization. Please * use {@link #withVirtualTime(Supplier, long)} instead. */ @Deprecated static FirstStep withVirtualTime(long n, Supplier> scenarioSupplier) { return withVirtualTime(scenarioSupplier, () -> VirtualTimeScheduler.getOrSet(), n); } /** * Prepare a new {@code StepVerifier} in a controlled environment using * {@link VirtualTimeScheduler} to schedule and expect virtual wait via * {@link Step#thenAwait}. Each {@link #verify()} will fully (re)play the * scenario. The verification will request a specified amount of values. * * @param scenarioSupplier a mandatory supplier of the {@link Publisher} to test * @param n the amount of items to request (must be >= 0) * @param the type of the subscriber * * @return a builder for setting up value expectations */ static FirstStep withVirtualTime(Supplier> scenarioSupplier, long n) { return withVirtualTime(scenarioSupplier, () -> VirtualTimeScheduler.getOrSet(), n); } /** * @deprecated to be removed in 3.1.0 for parameter ordering harmonization. Please * use {@link #withVirtualTime(Supplier, Supplier, long)} instead. */ @Deprecated static FirstStep withVirtualTime(long n, Supplier> scenarioSupplier, Supplier vtsLookup) { DefaultStepVerifierBuilder.checkPositive(n); Objects.requireNonNull(scenarioSupplier, "scenarioSupplier"); Objects.requireNonNull(vtsLookup, "vtsLookup"); StepVerifierOptions options = StepVerifierOptions.create() .initialRequest(n) .virtualTimeSchedulerSupplier(vtsLookup); return DefaultStepVerifierBuilder.newVerifier(options, scenarioSupplier); } /** * Create a new {@code StepVerifier} in a parameterized environment using * passed * {@link VirtualTimeScheduler} to schedule and expect virtual wait via * {@link Step#thenAwait}. Each {@link #verify()} will fully (re)play the * scenario. The verification will request a specified amount of values. * * @param scenarioSupplier a mandatory supplier of the {@link Publisher} to test * @param vtsLookup a mandatory {@link VirtualTimeScheduler} lookup to use in {@code * thenAwait} * @param n the amount of items to request (must be >= 0) * @param the type of the subscriber * * @return a builder for setting up value expectations */ static FirstStep withVirtualTime( Supplier> scenarioSupplier, Supplier vtsLookup, long n) { return withVirtualTime(scenarioSupplier, StepVerifierOptions.create() .initialRequest(n) .virtualTimeSchedulerSupplier(vtsLookup)); } /** * Create a new {@code StepVerifier} in a parameterized environment using * passed {@link VirtualTimeScheduler} to schedule and expect virtual wait via * {@link Step#thenAwait}. 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}. * * @param scenarioSupplier a mandatory supplier of the {@link Publisher} to test * @param options the verification options, including a mandatory * {@link VirtualTimeScheduler} lookup to use in {@code thenAwait} * @param the type of the subscriber * * @return a builder for setting up value expectations */ static FirstStep withVirtualTime( Supplier> scenarioSupplier, StepVerifierOptions options) { DefaultStepVerifierBuilder.checkPositive(options.getInitialRequest()); Objects.requireNonNull(options.getVirtualTimeSchedulerSupplier(), "vtsLookup"); Objects.requireNonNull(scenarioSupplier, "scenarioSupplier"); 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(); /** * Verify the signals received by this subscriber. This method will * block indefinitely until the stream has been terminated (either * through {@link Subscriber#onComplete()}, {@link Subscriber#onError(Throwable)} or * {@link Subscription#cancel()}). * * @return the {@link Duration} of the verification * * @throws AssertionError in case of expectation failures */ Duration verify() throws AssertionError; /** * Verify the signals received by this subscriber. This method will * block for the given duration or until the stream has been * terminated (either through {@link Subscriber#onComplete()}, * {@link Subscriber#onError(Throwable)} or * {@link Subscription#cancel()}). * * @return the {@link Duration} of the verification * * @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 this method will block indefinitely until the stream has * been terminated (either through {@link Subscriber#onComplete()}, * {@link Subscriber#onError(Throwable)} or {@link Subscription#cancel()}). * * @return the {@link Duration} of the verification * * @throws AssertionError in case of expectation failures */ Assertions verifyThenAssertThat(); /** * 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 * * @see Subscriber#onError(Throwable) */ StepVerifier expectError(); /** * Expect an error of the specified type. * * @param clazz the expected error type * * @return the built verification * * @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 * * @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 * * @see Subscriber#onError(Throwable) */ StepVerifier expectErrorMatches(Predicate predicate); /** * Expect the completion signal. * * @return the built verification * * @see Subscriber#onComplete() */ StepVerifier expectComplete(); /** * Cancel the underlying subscription. * * @return the built verification * * @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 * putting a timeout). * * @return the {@link Duration} of the verification * * @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 * putting a timeout). * * @param clazz the expected error type * @return the {@link Duration} of the verification * * @see #expectError(Class) * @see #verify() * @see Subscriber#onError(Throwable) */ Duration verifyError(Class clazz); /** * Trigger the {@link #verify() verification}, expecting an error with the * specified messagee 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 * putting a timeout). * * @param errorMessage the expected error message * @return the {@link Duration} of the verification * * @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 * putting a timeout). * * @param predicate the predicate to test on the next received error * @return the {@link Duration} of the verification * * @see #expectErrorMatches(Predicate) * @see #verify() * @see Subscriber#onError(Throwable) */ Duration verifyErrorMatches(Predicate predicate); /** * 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 * putting a timeout). * * @return the {@link Duration} of the verification * * @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} 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 consumeRecordedWith(Consumer> consumer); /** * 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) */ Step expectNext(T... ts); /** * Expect an element count starting from the last expectation or onSubscribe. * * @param count the predicate to test on the next received value * * @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 predicate to test on the next received value * * @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 no event has been observed by the verifier. A duration is * necessary to limit in time that "nothing" has effectively happened. * * @param duration the period to observe no event has been received * * @return this builder * * @see Subscriber */ Step expectNoEvent(Duration duration); /** * Expect and end a recording session started via {@link #recordWith} and * consume with * the * given consumer. * * @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 the session. *

If an * existing recording session hasn't not been declaratively consumed, this step * will override the current session. * * @param supplier the task to run * * @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}. * * @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 { /** * 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 Fuseable.QueueSubscription}. * * @return this builder * * @see Fuseable */ Step expectNoFusionSupport(); /** * Expect no Subscription or any other event for the given duration. * * @param duration the period to observe no event has been received * * @return this builder */ @Override FirstStep expectNoEvent(Duration duration); /** * 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 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 dropped at least one error to the * {@link Hooks#onErrorDropped(Consumer)} hook. */ Assertions hasDroppedErrors(); /** * 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>> 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>> 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); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy