RxJava: 2.x: Uncaught errors fail silently in junit tests.
I was writing a utility that involved decorating observers and catching uncaught errors from the delegate observer’s onError
handling when I noticed in testing that tests always passed. There would be log output, but tests would pass none the less.
Demo of the issue can be found here, but the gist of it is that the following test passes in standard JUnit:
@Test
public void blerg() {
Observable.error(new RuntimeException("This should fail the test"))
.subscribe();
}
I’m trying to understand why errors are passed directly to the handler rather than just thrown, and also curious to get thoughts on making this behavior configurable (if for no other reason than to disable in junit tests). I can’t find any details about why this behavior happens in JUnit either.
I think this has worrisome implications. In our codebase alone, I dropped in an error watching rule to track these and discovered over 150 tests with uncaught exceptions that are being marked as passing otherwise.
About this issue
- Original URL
- State: closed
- Created 7 years ago
- Reactions: 13
- Comments: 36 (29 by maintainers)
I think I’m not being clear. I’m not trying to test if
onXXX
methods throw. I’m concerned that errors in the stream with no error handling don’t actually fail tests because they’re sent directly to the handler rather than just throwing if there’s no plugin error handler. Trying to understand why we send directly to the handler.Write a custom
Subscriber
that throws from itsonError
method and subscribe with that instead of the emptysubscribe()
.We normally don’t have errors in streams and convert them to values using Kotlin sealed classes, so we don’t use
subscribe()
overloads with error handling, rxlint won’t help (and it’s Android specific).But we need to detect unexpected errors in tests.
And we have 3k+ test cases written with different test frameworks, hooking into each test class is manual and error-prone work. There are hundreds if not thousands other projects migrating from v1 to v2 that’ll have to do the same and this number will only grow.
Unlike 1.x,
OnErrorNotImplementedException
is not thrown. Also please read on the error handling section of the wiki.Okay, let’s see the proposed code changes.
This is actually a problem outside of tests too unfortunately. Say I’m on Android and I want to write a custom plugin for decorating observers (such as tracking analytics). I want to give the delegate observer a shot at
onError
and try/catch to react to how it handled it, but if it’s the default rxjava execution and hands it over to the current thread’s uncaught exception handler via its internaluncaught()
method, the app will just quite immediately without thecatch
clause getting a chance.Installing a custom error handler via plugins is not an option either, as it is just try/catch’d and pipes the error again through the above
uncaught()
pipeline.Could I propose making this configurable via system property? Something like
rx2.uncaughtBehavior
with values"default"
and"rethrow"
?Edit: Proposed a strawman impl in #5569
That’s what I want to avoid and throw only if RxJava v2 specific subscriber was used.
OnErrorNotImplementedException
can not be thrown in the middle of rx chain. Only by calling v2-specificsubscribe()
without error handler, which reduces possibility of multi-library problems since they will use RSorg.reactivestreams.Publisher.subscribe(org.reactivestreams.Subscriber)
.As said before, v2 violates some RS rules if v2 specific subscriber was used, but respects RS if RS subscriber comes. And I can create problematic multi-library flow because of that, but only if I’d like to.
Btw, this is already happening with RxJava v1 and v2 connected through interop library, v2 swallows
OnErrorNotImplemented
thrown by v1:Good point, but.
Scheduling is explicit, our code will not compile if we won’t pass scheduler (either real or test one).
While current design of error handling is implicit and not only allows tests to compile without checks suggested above, but also returns normally and tests pass as false positives.
2.x
Subscriber
s andObserver
s must not throw from theironXXX
methods. Install an error handler before the test and verify there was no undeliverable error. See how we do it in RxJava tests:https://github.com/ReactiveX/RxJava/blob/3bfc275ca2f8d14a67305bbadb7bbc6315205250/src/test/java/io/reactivex/parallel/ParallelPeekTest.java#L55