【问题标题】:RxJava2 with JUnit: no exceptions thrown in tests带有 JUnit 的 RxJava2:在测试中没有抛出异常
【发布时间】:2016-12-20 10:20:27
【问题描述】:

下面的代码在 JUnit 环境下运行时不会崩溃。但是在应用程序中运行时它会崩溃。我可以在控制台中看到错误日志,但测试被标记为通过。

  @Test
  public void test() {
    Observable observable = Observable.error(new RuntimeException());
    observable.subscribe();
  }

所以,问题是:如何让它在 JUnit 中崩溃。因为是的,如果某些东西在应用程序中不起作用,那么如果它在单元测试中也不起作用是一件好事:)

在这个例子中,我可以直接访问 observable。但在我的真实测试中,我没有。真正的 observables 只是被测试类的内部细节。我能做的最多的就是注入调度器什么的。

那么,如何在不直接访问 observable 的情况下使其崩溃?

另外,我刚刚检查过这段代码也没有崩溃:

  @Test
  public void test() {
    Observable observable = Observable.error(new RuntimeException());
    observable.subscribe(new Consumer() {
      @Override
      public void accept(Object o) throws Exception {
        throw new RuntimeException();
      }
    }, new Consumer<Throwable>() {
      @Override
      public void accept(Throwable throwable) throws Exception {
        throw new RuntimeException();
      }
    });
  }

【问题讨论】:

  • 这种用法在 2.x 中不再同步抛出,而是在插件处理程序中出现错误。默认情况下,处理程序会打印堆栈跟踪,这就是为什么您会看到错误打印到控制台但测试没有失败的原因。否则,只会传播诸如 StackOverflowError 之类的致命异常。
  • @akarnokd 谢谢,这很有趣,它是 RxJava2 特定的。有没有办法以某种方式检测这些错误并使测试失败?
  • 你必须为此设计。此外,您不应该真正投入消费者,如果可能的话,请在消费者的 onNext 或 lambda 中使用您自己的 try-catch 来解决有问题的代码。
  • @akarnokd 设计如何?我没有想法。我不投入消费者,这不太可能。但是我的应用程序中有几十个这样的消费者,并且会发生编程错误。尤其是在开发过程中。然后它们发生了,我希望能够立即抓住它们。在执行单元测试期间。
  • 在我们自己的单元测试中,我挂钩 onError 并验证是否有错误sent that way。否则,看看你的要点,我不明白你为什么要针对失败的消费者进行测试——这是他们的问题,你的代码被 RxJava 尽可能地屏蔽了。

标签: android junit rx-java rx-java2


【解决方案1】:

根据 akarnokd,这是 RxJava2 特有的问题。

“这样的用法在 2.x 中不再同步抛出,而是在插件处理程序中出现错误。”

可以检查此代码是否引发任何错误

public static List<Throwable> trackPluginErrors() {
    final List<Throwable> list = Collections.synchronizedList(new ArrayList<Throwable>());

    RxJavaPlugins.setErrorHandler(new Consumer<Throwable>() {
        @Override
        public void accept(Throwable t) {
            list.add(t);
        }
    });

    return list;
}

【讨论】:

    【解决方案2】:

    我用来解决这个问题的一个小技巧是创建一个 JUnit4 TestRule 类,该类设置一个自定义 RxJava 错误处理程序,以便在发生未处理的 RxJava 错误时抛出:

    
    /**
     * A rule that detects RxJava silent errors and reports them to JUnit
       (by throwing them).
     */
    public class RxErrorRule implements TestRule {
    
        @Override
        public Statement apply(Statement base, Description description) {
            return new Statement() {
                @Override
                public void evaluate() throws Throwable {
    
                    Consumer<? super Throwable> previous = RxJavaPlugins.getErrorHandler();
    
                    AtomicReference<Throwable> result = setupErrorHandler();
    
                    try {
                        base.evaluate();
                    } finally {
                        RxJavaPlugins.setErrorHandler(previous);
                    }
    
                    if (result.get() != null) {
                        throw result.get();
                    }
                }
            };
        }
    
        private AtomicReference<Throwable> setupErrorHandler() {
            AtomicReference<Throwable> result = new AtomicReference<>();
    
            RxJavaPlugins.setErrorHandler(new Consumer<Throwable>() {
                @Override
                public void accept(Throwable throwable) {
                    result.set(throwable);
                }
            });
            return result;
        }
    
    }
    

    在单元测试中:

    
    public class YourRxTest {
    
        @Rule
        public RxErrorRule errorRule = new RxErrorRule();
    
        // ...
    }
    

    【讨论】:

      【解决方案3】:

      使用TestSubscriber

          Observable observable = Observable.error(new RuntimeException());
          TestSubscriber testSubscriber = TestSubscriber.create();
      
          observable.subscribe(testSubscriber);
      
          testSubscriber.assertTerminalEvent();
          testSubscriber.assertError(RuntimeException.class);
      

      【讨论】:

      • 看起来不错,但不幸的是,在我的真实测试中,我无法访问 observable 和 subscribe() 调用。我不是在测试 observable,我在测试由 observable 组成的组件。
      • 而且,我想测试真实订阅者的代码是否可以正常工作,所以用 TestSubscriber 替换它只会达到目的。
      • 为什么要测试一个类的内部细节?您应该只测试类的公共接口。
      • 你为什么认为我在测试一个类的内部细节? Observables 和订阅者是我的课程的内部细节。当我在测试类的公共接口时,当内部细节发生崩溃时,我希望我的测试失败。
      • 下面是更好的例子:gist.github.com/rongi/16c2f5e4b494b0671c695007daa70984我的演示者在观察结果时崩溃,但我的测试通过了。
      猜你喜欢
      • 2020-12-21
      • 2013-07-17
      • 1970-01-01
      • 2018-09-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-01-17
      • 1970-01-01
      相关资源
      最近更新 更多