Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

2.x: Add error assertion with predicate to TestSubscriber and TestObserver #4586

Merged
merged 2 commits into from
Sep 23, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 58 additions & 1 deletion src/main/java/io/reactivex/observers/TestObserver.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import io.reactivex.disposables.Disposable;
import io.reactivex.exceptions.CompositeException;
import io.reactivex.functions.Consumer;
import io.reactivex.functions.Predicate;
import io.reactivex.internal.disposables.DisposableHelper;
import io.reactivex.internal.functions.ObjectHelper;
import io.reactivex.internal.fuseable.QueueDisposable;
Expand Down Expand Up @@ -424,10 +425,12 @@ public final TestObserver<T> assertNoErrors() {
*
* <p>The comparison is performed via Objects.equals(); since most exceptions don't
* implement equals(), this assertion may fail. Use the {@link #assertError(Class)}
* overload to test against the class of an error instead of an instance of an error.
* overload to test against the class of an error instead of an instance of an error
* or {@link #assertError(Predicate)} to test with different condition.
* @param error the error to check
* @return this;
* @see #assertError(Class)
* @see #assertError(Predicate)
*/
public final TestObserver<T> assertError(Throwable error) {
int s = errors.size();
Expand Down Expand Up @@ -475,6 +478,43 @@ public final TestObserver<T> assertError(Class<? extends Throwable> errorClass)
return this;
}

/**
* Asserts that this TestObserver received exactly one onError event for which
* the provided predicate returns true.
* @param errorPredicate
* the predicate that receives the error Throwable
* and should return true for expected errors.
* @return this
*/
public final TestObserver<T> assertError(Predicate<Throwable> errorPredicate) {
int s = errors.size();
if (s == 0) {
throw fail("No errors");
}

boolean found = false;

for (Throwable e : errors) {
try {
if (errorPredicate.test(e)) {
found = true;
break;
}
} catch (Exception ex) {
throw ExceptionHelper.wrapOrThrow(ex);
}
}

if (found) {
if (s != 1) {
throw fail("Error present but other errors as well");
}
} else {
throw fail("Error not present");
}
return this;
}

/**
* Assert that this TestObserver received exactly one onNext value which is equal to
* the given value with respect to Objects.equals.
Expand Down Expand Up @@ -825,6 +865,7 @@ public final TestObserver<T> assertOf(Consumer<? super TestObserver<T>> check) {
* @param values the expected values, asserted in order
* @return this
* @see #assertFailure(Class, Object...)
* @see #assertFailure(Predicate, Object...)
* @see #assertFailureAndMessage(Class, String, Object...)
*/
public final TestObserver<T> assertResult(T... values) {
Expand All @@ -848,6 +889,22 @@ public final TestObserver<T> assertFailure(Class<? extends Throwable> error, T..
.assertNotComplete();
}

/**
* Assert that the upstream signalled the specified values in order and then failed
* with a Throwable for which the provided predicate returns true.
* @param errorPredicate
* the predicate that receives the error Throwable
* and should return true for expected errors.
* @param values the expected values, asserted in order
* @return this
*/
public final TestObserver<T> assertFailure(Predicate<Throwable> errorPredicate, T... values) {
return assertSubscribed()
.assertValues(values)
.assertError(errorPredicate)
.assertNotComplete();
}

/**
* Assert that the upstream signalled the specified values in order,
* then failed with a specific class or subclass of Throwable
Expand Down
59 changes: 58 additions & 1 deletion src/main/java/io/reactivex/subscribers/TestSubscriber.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import io.reactivex.disposables.Disposable;
import io.reactivex.exceptions.CompositeException;
import io.reactivex.functions.Consumer;
import io.reactivex.functions.Predicate;
import io.reactivex.internal.functions.ObjectHelper;
import io.reactivex.internal.fuseable.QueueSubscription;
import io.reactivex.internal.subscriptions.SubscriptionHelper;
Expand Down Expand Up @@ -468,10 +469,12 @@ public final TestSubscriber<T> assertNoErrors() {
*
* <p>The comparison is performed via Objects.equals(); since most exceptions don't
* implement equals(), this assertion may fail. Use the {@link #assertError(Class)}
* overload to test against the class of an error instead of an instance of an error.
* overload to test against the class of an error instead of an instance of an error
* or {@link #assertError(Predicate)} to test with different condition.
* @param error the error to check
* @return this
* @see #assertError(Class)
* @see #assertError(Predicate)
*/
public final TestSubscriber<T> assertError(Throwable error) {
int s = errors.size();
Expand Down Expand Up @@ -519,6 +522,43 @@ public final TestSubscriber<T> assertError(Class<? extends Throwable> errorClass
return this;
}

/**
* Asserts that this TestSubscriber received exactly one onError event for which
* the provided predicate returns true.
* @param errorPredicate
* the predicate that receives the error Throwable
* and should return true for expected errors.
* @return this
*/
public final TestSubscriber<T> assertError(Predicate<Throwable> errorPredicate) {
int s = errors.size();
if (s == 0) {
throw fail("No errors");
}

boolean found = false;

for (Throwable e : errors) {
try {
if (errorPredicate.test(e)) {
found = true;
break;
}
} catch (Exception ex) {
throw ExceptionHelper.wrapOrThrow(ex);
}
}

if (found) {
if (s != 1) {
throw fail("Error present but other errors as well");
}
} else {
throw fail("Error not present");
}
return this;
}

/**
* Assert that this TestSubscriber received exactly one onNext value which is equal to
* the given value with respect to Objects.equals.
Expand Down Expand Up @@ -869,6 +909,7 @@ public final TestSubscriber<T> assertOf(Consumer<? super TestSubscriber<T>> chec
* @param values the expected values, asserted in order
* @return this
* @see #assertFailure(Class, Object...)
* @see #assertFailure(Predicate, Object...)
* @see #assertFailureAndMessage(Class, String, Object...)
*/
public final TestSubscriber<T> assertResult(T... values) {
Expand All @@ -892,6 +933,22 @@ public final TestSubscriber<T> assertFailure(Class<? extends Throwable> error, T
.assertNotComplete();
}

/**
* Assert that the upstream signalled the specified values in order and then failed
* with a Throwable for which the provided predicate returns true.
* @param errorPredicate
* the predicate that receives the error Throwable
* and should return true for expected errors.
* @param values the expected values, asserted in order
* @return this
*/
public final TestSubscriber<T> assertFailure(Predicate<Throwable> errorPredicate, T... values) {
return assertSubscribed()
.assertValues(values)
.assertError(errorPredicate)
.assertNotComplete();
}

/**
* Assert that the upstream signalled the specified values in order,
* then failed with a specific class or subclass of Throwable
Expand Down
52 changes: 52 additions & 0 deletions src/test/java/io/reactivex/observers/TestObserverTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import io.reactivex.disposables.*;
import io.reactivex.exceptions.TestException;
import io.reactivex.functions.*;
import io.reactivex.internal.functions.Functions;
import io.reactivex.internal.fuseable.QueueDisposable;
import io.reactivex.internal.operators.observable.ObservableScalarXMap.ScalarDisposable;
import io.reactivex.internal.subscriptions.EmptySubscription;
Expand Down Expand Up @@ -332,6 +333,13 @@ public void assertError() {
// expected
}

try {
ts.assertError(Functions.<Throwable>alwaysTrue());
throw new RuntimeException("Should have thrown");
} catch (AssertionError ex) {
// expected
}

try {
ts.assertErrorMessage("");
throw new RuntimeException("Should have thrown");
Expand Down Expand Up @@ -367,6 +375,15 @@ public void assertError() {

ts.assertError(TestException.class);

ts.assertError(Functions.<Throwable>alwaysTrue());

ts.assertError(new Predicate<Throwable>() {
@Override
public boolean test(Throwable t) throws Exception {
return t.getMessage() != null && t.getMessage().contains("Forced");
}
});

ts.assertErrorMessage("Forced failure");

try {
Expand All @@ -390,6 +407,13 @@ public void assertError() {
// expected
}

try {
ts.assertError(Functions.<Throwable>alwaysFalse());
throw new RuntimeException("Should have thrown");
} catch (AssertionError exc) {
// expected
}

try {
ts.assertNoErrors();
throw new RuntimeException("Should have thrown");
Expand Down Expand Up @@ -428,12 +452,16 @@ public void assertFailure() {

ts.assertFailure(TestException.class);

ts.assertFailure(Functions.<Throwable>alwaysTrue());

ts.assertFailureAndMessage(TestException.class, "Forced failure");

ts.onNext(1);

ts.assertFailure(TestException.class, 1);

ts.assertFailure(Functions.<Throwable>alwaysTrue(), 1);

ts.assertFailureAndMessage(TestException.class, "Forced failure", 1);
}

Expand Down Expand Up @@ -965,6 +993,12 @@ public void assertErrorMultiple() {
} catch (AssertionError ex) {
// expected
}
try {
ts.assertError(Functions.<Throwable>alwaysTrue());
throw new RuntimeException("Should have thrown!");
} catch (AssertionError ex) {
// expected
}
try {
ts.assertErrorMessage("");
throw new RuntimeException("Should have thrown!");
Expand All @@ -973,6 +1007,24 @@ public void assertErrorMultiple() {
}
}

@Test
public void testErrorInPredicate() {
TestObserver<Object> ts = new TestObserver<Object>();
ts.onError(new RuntimeException());
try {
ts.assertError(new Predicate<Throwable>() {
@Override
public boolean test(Throwable throwable) throws Exception {
throw new TestException();
}
});
} catch (TestException ex) {
// expected
return;
}
fail("Error in predicate but not thrown!");
}

@Test
public void assertComplete() {
TestObserver<Integer> ts = new TestObserver<Integer>();
Expand Down
Loading