Search code examples
androidjunitrx-javarx-java2

RxJava2 with JUnit: no exceptions thrown in tests


The code below won't crash when running in JUnit environment. But it crashes when running in the app. I can see error logs in the console, but tests are marked as passed.

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

So, the question is: how to make it crash in JUnit. Because yeah, if something doesn't work in the app it's a good thing if it doesn't work in the unit tests also :)

And in this example I have direct access to the observable. But in my real tests I don't have that. Real observables are just internal details of classes that being tested. The most thing I can to do is to inject schedulers or something.

So, how to make it crash without having direct access to the observable?

Also, I've just checked this code doesn't crash either:

  @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();
      }
    });
  }

Solution

  • A small trick I use to tackle this problem, is creating a JUnit4 TestRule class that setups a custom RxJava error handler so it can throw when an unhandled RxJava error occurs:

    
    /**
     * 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;
        }
    
    }
    

    And in the unit test:

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