Reputation: 81
I was given a task to make implementation using RxJava, and safeguard in such way, that if any error happens, it gets wrapped into custom exception.
Problem is, that regardless of what I do RxJavaPlugin decides to wrap my exception into CompositeException even when there is only one. Which fails tests.
I've tried everything I could find on the google all the way up to actually overwriting RxJavaPlugin's global error handler, but it ignores attempted changes.
implementation of function that is supposed to throw it at the moment of writing this post
Single<BigDecimal> summarizeConfirmedTransactions() throws SummarizationException {
try{
Observable<Transaction> observableTransactions = transactions.get()
.doOnError(throwable -> {
Exceptions.propagate(throwable);
});
Observable<Confirmation> observableConfirmations = confirmations.get()
.doOnError(throwable -> {
Exceptions.propagate(throwable);
});
return Observable.zip(observableTransactions, observableConfirmations, (t, c) -> new ConfirmableTransaction(t.transactionId, c.isConfirmed, t.value))
.filter(confirmableTransaction -> confirmableTransaction.isConfirmed)
.map(t -> t.value)
.reduce(BigDecimal::add)
.toSingle()
.doOnError(throwable -> {
Exceptions.propagate(throwable);
});
}catch(Exception e)
{
throw new SummarizationException(e.getMessage());
}
Assertion in test fails because exception ultimately thrown is CompositeException with single exception inside of it. and I am required to have it be of SummarizationException.class
big thanks in advance.
Edit: On request, here is code used to test the solution. I am not allowed to modify this one.
@Test
public void shouldWrapErrorIntoException() {
final ConfirmedTransactionSummarizer summarizer =
new ConfirmedTransactionSummarizer(ALL_CONFIRMED::transactions, () -> Observable.error(new RuntimeException("Booom")));
summarizer
.summarizeConfirmedTransactions()
.subscribe(testObserver);
testObserver.assertError(SummarizationException.class);
testObserver.assertErrorMessage("Booom");
}
PS. I've asked the giver of the task, he said that "I'm the only one with such problem" and that I should not overcomplicate things and go for easiest solution.... which results in composite exception of 3 exceptions - one of which is my exception wrap and other two are instances of RuntimeException inserted by test code.
Upvotes: 4
Views: 2895
Reputation: 81
Ok, so after a little bit of more digging, and with helpful tip from a friend I've managed to nail down intention of the task:
What I was supposed to do there was:
Single<BigDecimal> summarizeConfirmedTransactions() throws SummarizationException {
Observable<Transaction> observableTransactions = transactions.get();
Observable<Confirmation> observableConfirmations = confirmations.get();
return Observable.zip(observableTransactions, observableConfirmations,
(t, c) -> new ConfirmableTransaction(c.isConfirmed, t.value))
.filter(confirmableTransaction -> confirmableTransaction.isConfirmed)
.map(t -> t.value)
.reduce(BigDecimal::add)
.toSingle()
.onErrorResumeNext(th -> Single.error(new SummarizationException(th.getMessage())));
}
TL:DR I was not supposed to "wrap" errors into thrown exception but wrap them into error response containing exception.
Upvotes: 4
Reputation: 439
One way to handle this is by using try-catch block in your test, unwraping CompositeException and then asserting caught exception.
fun testSummarizationException() {
try {
summarizeConfirmedTransactions()
} catch (ex: Exception) {
val customException = (ex as? CompositeException)?.exceptions?.get(0)
// assert to make sure thrown exception is of custom type
assert(customException is SummarizationException)
}
}
This is where CompositeException is unwrapped to get custom exception.
val customException = (ex as? CompositeException)?.exceptions?.get(0)
Exception is type-casted to CompositeException if it's permissible. If casting is not allowed for this type, this will return null and fails the test.
Upvotes: 0