diff --git a/src/main/java/rx/Single.java b/src/main/java/rx/Single.java index 3701d93189..6817a4e283 100644 --- a/src/main/java/rx/Single.java +++ b/src/main/java/rx/Single.java @@ -1864,4 +1864,38 @@ public void onNext(T t) { return lift(new OperatorDoOnEach(observer)); } + + /** + * Modifies the source {@link Single} so that it invokes an action when it calls {@code onSuccess}. + *

+ * + *

+ *
Scheduler:
+ *
{@code doOnSuccess} does not operate by default on a particular {@link Scheduler}.
+ *
+ * + * @param onSuccess + * the action to invoke when the source {@link Single} calls {@code onSuccess} + * @return the source {@link Single} with the side-effecting behavior applied + * @see ReactiveX operators documentation: Do + */ + @Experimental + public final Single doOnSuccess(final Action1 onSuccess) { + Observer observer = new Observer() { + @Override + public void onCompleted() { + } + + @Override + public void onError(Throwable e) { + } + + @Override + public void onNext(T t) { + onSuccess.call(t); + } + }; + + return lift(new OperatorDoOnEach(observer)); + } } diff --git a/src/test/java/rx/SingleTest.java b/src/test/java/rx/SingleTest.java index f78151b094..de1a38f0ca 100644 --- a/src/test/java/rx/SingleTest.java +++ b/src/test/java/rx/SingleTest.java @@ -16,6 +16,7 @@ import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -570,4 +571,81 @@ public void shouldPassErrorFromCallable() throws Exception { verify(callable).call(); } + + @Test + public void doOnSuccessShouldInvokeAction() { + Action1 action = mock(Action1.class); + + TestSubscriber testSubscriber = new TestSubscriber(); + + Single + .just("value") + .doOnSuccess(action) + .subscribe(testSubscriber); + + testSubscriber.assertValue("value"); + testSubscriber.assertNoErrors(); + + verify(action).call(eq("value")); + } + + @Test + public void doOnSuccessShouldPassErrorFromActionToSubscriber() { + Action1 action = mock(Action1.class); + + Throwable error = new IllegalStateException(); + doThrow(error).when(action).call(eq("value")); + + TestSubscriber testSubscriber = new TestSubscriber(); + + Single + .just("value") + .doOnSuccess(action) + .subscribe(testSubscriber); + + testSubscriber.assertNoValues(); + testSubscriber.assertError(error); + + verify(action).call(eq("value")); + } + + @Test + public void doOnSuccessShouldNotCallActionIfSingleThrowsError() { + Action1 action = mock(Action1.class); + + Throwable error = new IllegalStateException(); + + TestSubscriber testSubscriber = new TestSubscriber(); + + Single + .error(error) + .doOnSuccess(action) + .subscribe(testSubscriber); + + testSubscriber.assertNoValues(); + testSubscriber.assertError(error); + + verifyZeroInteractions(action); + } + + @Test + public void doOnSuccessShouldNotSwallowExceptionThrownByAction() { + Action1 action = mock(Action1.class); + + Throwable exceptionFromAction = new IllegalStateException(); + + doThrow(exceptionFromAction).when(action).call(eq("value")); + + TestSubscriber testSubscriber = new TestSubscriber(); + + Single + .just("value") + .doOnSuccess(action) + .subscribe(testSubscriber); + + testSubscriber.assertNoValues(); + testSubscriber.assertError(exceptionFromAction); + + verify(action).call(eq("value")); + } }