Reactor响应式编程系列(十)- Reactor测试类StepVerifier的源码解析

Reactor响应式编程系列(十)- Reactor测试类StepVerifier的源码解析

Reactor响应式编程系列导航

前言

至于为什么先学习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();
}

总结:

  1. StepVerifier类中定义了许多接口,分别负责源、元素下发过程、运行结束等验证的相关方法。
  2. 通过StepVerifier.create()去创建源以及一系列动作的时候,会调用DefaultStepVerifierBuilder()将一系列的动作包装成默认的SignalEvent事件,并加入到其中的script列表中。
  3. SignalEvent事件的包装则基于Signal接口来做一个预期处理,每个事件都会有一些列的动作,将其封装为一个Function
  4. 最后统一的对集合中的事件进行遍历调用,所有的验证逻辑围绕着Signal接口中的accept()方法来展开。
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Zong_0915

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值