Reactor响应式编程系列(十)- Reactor测试类StepVerifier的源码解析
前言
至于为什么先学习StepVerifier
的源码而不是使用,因为在前面的文章中,一些案例还是有涉及到其使用的。如果先把其原理弄懂了,那么其使用起来肯定也是比较容易上手的。
StepVerifier源码解析
StepVerifier
主要用来测试一个序列,从源的创建、下发、结束等方面都有涉及,因此先看下其接口的设计是怎样的。
接口定义
1.StepVerifier
的使用中,每个测试都会有针对结束操作的测试,因此先看下结束操作的相关接口:reactor.test.StepVerifier.LastStep
,该接口主要用于验证异常结束或者正常结束,并对上游源下发的error事件进行处理,来看下相关结束接口:
public interface LastStep {
StepVerifier consumeErrorWith(Consumer<Throwable> var1);
// 期望上游源下发的是一个Error事件,其返回值为等待被验证的脚本对象。
StepVerifier expectError();
// 在上面方法的基础上,可以指定对应Error事件的类型
StepVerifier expectError(Class<? extends Throwable> var1);
// 期望下发的Error事件中所携带的具体信息所指定的内容
StepVerifier expectErrorMessage(String var1);
// 期望下发的Error事件,通过条件表达式对其进行判断
StepVerifier expectErrorMatches(Predicate<Throwable> var1);
// 触发验证,同时期望上游源以下发一个Error事件为结束的事件
Duration verifyError();
//...等
}
2.对于下发元素的测试接口,则通过Step
接口来将这些验证操作进行聚合:
public interface Step<T> extends StepVerifier.LastStep
3.对于上游源生产元素的相关测试,则通过FirstStep
接口来完成。
public interface FirstStep<T> extends StepVerifier.Step<T>
4.对于订阅者的Context
相关测试,则通过ContextExpectations
接口完成。
public interface ContextExpectations<T>
接口实现
在接口定义环节中,我们可以看到,针对:结束、过程、源发布等环节都有对应的验证方法,React中,是将订阅过程中产生的各种形式的内容组合在一起,将每个测试内容包装成一个事件并加入到容器当中(如队列、集合),最后在验证的时候遍历容器中存储的事件,依次执行。
用过StepVerifier
的小伙伴都知道,其使用一般以StepVerifier.create(xxx)
为开头,那么就来看下它的create()
方法:
static <T> StepVerifier.FirstStep<T> create(Publisher<? extends T> publisher) {
return create(publisher, 9223372036854775807L);
}
↓↓↓↓↓↓↓
static <T> StepVerifier.FirstStep<T> create(Publisher<? extends T> publisher, long n) {
return create(publisher, StepVerifierOptions.create().initialRequest(n));
}
↓↓↓↓↓↓↓
static <T> StepVerifier.FirstStep<T> create(Publisher<? extends T> publisher, StepVerifierOptions options) {
return DefaultStepVerifierBuilder.newVerifier(options, () -> {
return publisher;
});
}
↓↓↓↓↓↓↓
final class DefaultStepVerifierBuilder<T> implements FirstStep<T> {
static Duration defaultVerifyTimeout = StepVerifier.DEFAULT_VERIFY_TIMEOUT;
final SignalEvent<T> defaultFirstStep;
// 列表,包含我们所需的预期事件
final List<Event<T>> script;
static <T> FirstStep<T> newVerifier(StepVerifierOptions options, Supplier<? extends Publisher<? extends T>> scenarioSupplier) {
checkPositive(options.getInitialRequest());
Objects.requireNonNull(scenarioSupplier, "scenarioSupplier");
return new DefaultStepVerifierBuilder(options, scenarioSupplier);
}
DefaultStepVerifierBuilder(StepVerifierOptions options,
@Nullable Supplier<? extends Publisher<? extends T>> sourceSupplier) {
// ...
// 由此可知,添加的默认事件类型是SignalEvent,这些事件在Reactor中统一由Signal接口标识
this.script.add(defaultFirstStep);
}
可以发现列表中的事件,其类型默认是SignalEvent
,因为这些事件在Reactor
中统一由Signal
接口进行标识,所以SignalEvent
会基于Signal
来做预期处理逻辑的包装(即上文提到的将每个测试内容包装成一个事件),相关方法如下:
static <T> SignalEvent<T> newOnSubscribeStep(MessageFormatter messageFormatter, String desc){
return new SignalEvent<>((signal, se) -> {
if (!signal.isOnSubscribe()) {
return messageFormatter.failOptional(se, "expected: onSubscribe(); actual: %s", signal);
}
else {
return Optional.empty();
}
}, desc);
}
相关内部类SignalEvent
的定义如下:
static final class SignalEvent<T> extends AbstractSignalEvent<T> {
final BiFunction<Signal<T>, SignalEvent<T>, Optional<AssertionError>> function;
SignalEvent(BiFunction<Signal<T>, SignalEvent<T>, Optional<AssertionError>> function,
String desc) {
super(desc);
// 在生成对应SignalEvent实例的时候,会根据需要来定义对应的Function
// 这里的function就代表了下发的一系列的事件动作
this.function = function;
}
}
而Signal
接口设计的核心为accept()
方法,可以发现里面主要是关于订阅者接口设计的几个方法,而在整个下发过程中,所有的逻辑也是围绕着这几个方法来展开的。
@Override
default void accept(Subscriber<? super T> observer) {
if (isOnNext()) {
observer.onNext(get());
}
else if (isOnComplete()) {
observer.onComplete();
}
else if (isOnError()) {
observer.onError(getThrowable());
}
else if (isOnSubscribe()) {
observer.onSubscribe(getSubscription());
}
}
以LastStep
接口的一个方法实现为例:
@Override
public DefaultStepVerifier<T> expectError(Class<? extends Throwable> clazz) {
Objects.requireNonNull(clazz, "clazz");
// 定义了一个SignalEvent类型的实例
SignalEvent<T> event = new SignalEvent<>((signal, se) -> {
// 倘若下发的并不是onError,比如是个异常,那么会调用fail方法,并返回结果
if (!signal.isOnError()) {
return messageFormatter.failOptional(se, "expected: onError(%s); actual: %s",
clazz.getSimpleName(), signal);
}
else if (!clazz.isInstance(signal.getThrowable())) {
return messageFormatter.failOptional(se, "expected error of type: %s; actual type: %s",
clazz.getSimpleName(), signal.getThrowable());
}
else {
return Optional.empty();
}
}, "expectError(Class)");
this.script.add(event);
// 将该实例存储于this.script列表中。
return build();
}
总结:
StepVerifier
类中定义了许多接口,分别负责源、元素下发过程、运行结束等验证的相关方法。- 通过
StepVerifier.create()
去创建源以及一系列动作的时候,会调用DefaultStepVerifierBuilder()
,将一系列的动作包装成默认的SignalEvent
事件,并加入到其中的script
列表中。 SignalEvent
事件的包装则基于Signal
接口来做一个预期处理,每个事件都会有一些列的动作,将其封装为一个Function
。- 最后统一的对集合中的事件进行遍历调用,所有的验证逻辑围绕着
Signal
接口中的accept()
方法来展开。