Java 使用repeat()测试订阅
我想测试一个函数,该函数在延迟单声道上使用Java 使用repeat()测试订阅,java,project-reactor,Java,Project Reactor,我想测试一个函数,该函数在延迟单声道上使用repeat()操作符并订阅结果。在测试中,我使用TestPublisher模拟mono中的新值 以非常简化的形式,它看起来如下所示: package de.cronos.mad.messages.backend; 导入java.util.function.Supplier; 导入org.junit.jupiter.api.Test; 导入reactor.core.publisher.Mono; 导入reactor.test.publisher.Test
repeat()
操作符并订阅结果。在测试中,我使用TestPublisher
模拟mono中的新值
以非常简化的形式,它看起来如下所示:
package de.cronos.mad.messages.backend;
导入java.util.function.Supplier;
导入org.junit.jupiter.api.Test;
导入reactor.core.publisher.Mono;
导入reactor.test.publisher.TestPublisher;
公共类重复测试{
私有静态类TestSubject{
公共无效logMonoValues(供应商单供应商){
Mono.defer(monoSupplier.repeat().subscribe(System.out::println);
}
}
@试验
公共测试(){
TestPublisher TestPublisher=TestPublisher.create();
TestSubject TestSubject=新的TestSubject();
logMonoValues(testPublisher::mono);
emit(“Hello”);
emit(“世界”);
}
}
将“Hello”记录到stdout后,执行将挂起。我想我理解了为什么会发生这种情况:emit(…)
调用从主线程开始,并从那里“驱动”订阅
我不知道的是如何修改此测试以使其完成,即不挂起?TestPublisher
emit
方法接受值数组。一旦发射,它将关闭源。所以你不能一个接一个的发射。而是像这样传递所有值
testPublisher.emit("Hello", "world");
// from TestPublisher emit
public final TestPublisher<T> emit(T... values) {
Objects.requireNonNull(values, "values array is null, please cast to T if null T required");
Object[] var2 = values;
int var3 = values.length;
for(int var4 = 0; var4 < var3; ++var4) {
T t = var2[var4];
this.next(t);
}
return this.complete();
}
就这样改变它来理解它的行为
Mono.defer(monoSupplier).repeat(10).subscribe(System.out::println);
注:
.repeat()
运算符在没有参数的情况下使用意味着无限重复。一旦原始订阅成功完成,它将立即启动新订阅。终止该协议的两种方式是:
Mono.error(Throwable)
或仅通过引发异常
testPublisher::mono
声明testPublisher
的此实例遵守mono
契约,即只发送一个值
因此,无论是两次调用.emit()
,还是使用两个参数调用它,都不会有帮助。额外的值将被忽略
repeat()
操作员创建的订阅会立即接收到重播的终止信号,这反过来又会导致repeat()
以紧密循环的方式重新订阅
Mono.defer()
或Mono.fromSupplier()
为.repeat()
创建的每个订阅创建一个新值。例如:
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.function.Supplier;
import reactor.core.publisher.Mono;
public class So63117029 {
private static class TestSubject {
public void logMonoValues(Supplier<Mono<String>> monoSupplier) {
Mono.defer(monoSupplier).repeat().subscribe(System.out::println);
}
}
public static void main(String[] args) {
LinkedBlockingQueue<String> data = new LinkedBlockingQueue<>(List.of("Hello", "World"));
TestSubject testSubject = new TestSubject();
testSubject.logMonoValues(() -> Mono.fromSupplier(data::remove));
}
}
import java.util.List;
导入java.util.concurrent.LinkedBlockingQueue;
导入java.util.function.Supplier;
导入reactor.core.publisher.Mono;
公共类So63117029{
私有静态类TestSubject{
公共无效logMonoValues(供应商单供应商){
Mono.defer(monoSupplier.repeat().subscribe(System.out::println);
}
}
公共静态void main(字符串[]args){
LinkedBlockingQueue数据=新的LinkedBlockingQueue(List.of(“Hello”,“World”));
TestSubject TestSubject=新的TestSubject();
testSubject.logMonoValues(()->Mono.fromSupplier(数据::remove));
}
}
当队列中没有更多数据时,方法
Queue.remove()
抛出一个NoSuchElementException
。您可以共享TestPublisher
吗?我用DirectProcessor替换了TestPublisher。它工作得很好。@vins:当然,它来自官方测试包(),并在文档()中进行了描述。@vins:我试图按照您的建议,用DirectProcessor替换TestPublisher,但stdout是空的(尽管测试已完成)。我是这样实现的:(注意TestPublisher的emit()
意味着next()
+complete()
)。@vins:我不小心使用了一个并行调度程序,导致测试在没有写入stdout的情况下完成。删除它之后,代码就像使用TestPublisher时一样挂起。“一旦发出,它就会关闭源代码”:这正是问题所在。如果源完成,这将导致repeat
操作员重新订阅。在实际用例中,这将导致发出http调用。这就是我想在测试中模拟的行为。@David,你的理解是对的。但请注意,您有两个不同的来源。一个是TestPublisher,另一个是Mono。您对monosource执行重复操作,这又取决于测试发布者。重复将重新订阅mono。但是它没有任何东西可以发射,因为它的父源已经完成。请注意,我使用的是Mono.defer
,它接受Mono供应商。每次重新订阅时,都会呼叫供应商并返回新的Mono。但是您是对的:TestPublisher已经完成,再次调用它并不会改变这一点。尽管如此,简单地next
ing多个值并不能测试测试对象的重复机制。这几乎是存在的,尽管正如您所说,抛出了一个NoTouchElementException
,这使测试失败。我使用takeUntilOther(…)
稍微扩展了我的测试,以停止无限repeat()
。我把工作结果概括为一个要点:
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.function.Supplier;
import reactor.core.publisher.Mono;
public class So63117029 {
private static class TestSubject {
public void logMonoValues(Supplier<Mono<String>> monoSupplier) {
Mono.defer(monoSupplier).repeat().subscribe(System.out::println);
}
}
public static void main(String[] args) {
LinkedBlockingQueue<String> data = new LinkedBlockingQueue<>(List.of("Hello", "World"));
TestSubject testSubject = new TestSubject();
testSubject.logMonoValues(() -> Mono.fromSupplier(data::remove));
}
}