Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/305.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 使用repeat()测试订阅_Java_Project Reactor - Fatal编程技术网

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()
    运算符在没有参数的情况下使用意味着无限重复。一旦原始订阅成功完成,它将立即启动新订阅。终止该协议的两种方式是:

  • 限制迭代次数,如按VIN建议重复(10)

  • 通过发送错误信号中止序列,例如使用
    Mono.error(Throwable)
    或仅通过引发异常

  • 调用
    testPublisher::mono
    声明
    testPublisher
    的此实例遵守
    mono
    契约,即只发送一个值

    因此,无论是两次调用
    .emit()
    ,还是使用两个参数调用它,都不会有帮助。额外的值将被忽略

  • javadoc说

    TestPublisher通常很热,[…]将第一个终止信号重播给后续订户

    这意味着迟到的用户会立即收到终止信号。由
    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));
      }
    }