From 89ab152ef7fe2f4e6c5c3537726bbdf704fd2772 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1vid=20Karnok?= Date: Thu, 12 May 2016 22:29:12 +0200 Subject: [PATCH 1/4] 1.x: TestSubscriber extra info on assertion failures --- src/main/java/rx/observers/TestObserver.java | 50 ++++++++++++---- .../java/rx/observers/TestSubscriber.java | 20 +++---- .../java/rx/observers/TestSubscriberTest.java | 60 ++++++++++++++++++- 3 files changed, 107 insertions(+), 23 deletions(-) diff --git a/src/main/java/rx/observers/TestObserver.java b/src/main/java/rx/observers/TestObserver.java index 928956980b..1f806910c6 100644 --- a/src/main/java/rx/observers/TestObserver.java +++ b/src/main/java/rx/observers/TestObserver.java @@ -15,12 +15,11 @@ */ package rx.observers; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; +import java.util.*; import rx.Notification; import rx.Observer; +import rx.exceptions.CompositeException; /** * Observer usable for unit testing to perform assertions, inspect received events or wrap a mocked Observer. @@ -113,7 +112,7 @@ public List getEvents() { */ public void assertReceivedOnNext(List items) { if (onNextEvents.size() != items.size()) { - throw new AssertionError("Number of items does not match. Provided: " + items.size() + " Actual: " + onNextEvents.size() + assertionError("Number of items does not match. Provided: " + items.size() + " Actual: " + onNextEvents.size() + ".\n" + "Provided values: " + items + "\n" @@ -126,10 +125,10 @@ public void assertReceivedOnNext(List items) { if (expected == null) { // check for null equality if (actual != null) { - throw new AssertionError("Value at index: " + i + " expected to be [null] but was: [" + actual + "]"); + assertionError("Value at index: " + i + " expected to be [null] but was: [" + actual + "]"); } } else if (!expected.equals(actual)) { - throw new AssertionError("Value at index: " + i + assertionError("Value at index: " + i + " expected to be [" + expected + "] (" + expected.getClass().getSimpleName() + ") but was: [" + actual + "] (" + (actual != null ? actual.getClass().getSimpleName() : "null") + ")"); @@ -146,22 +145,53 @@ public void assertReceivedOnNext(List items) { */ public void assertTerminalEvent() { if (onErrorEvents.size() > 1) { - throw new AssertionError("Too many onError events: " + onErrorEvents.size()); + assertionError("Too many onError events: " + onErrorEvents.size()); } if (onCompletedEvents.size() > 1) { - throw new AssertionError("Too many onCompleted events: " + onCompletedEvents.size()); + assertionError("Too many onCompleted events: " + onCompletedEvents.size()); } if (onCompletedEvents.size() == 1 && onErrorEvents.size() == 1) { - throw new AssertionError("Received both an onError and onCompleted. Should be one or the other."); + assertionError("Received both an onError and onCompleted. Should be one or the other."); } if (onCompletedEvents.size() == 0 && onErrorEvents.size() == 0) { - throw new AssertionError("No terminal events received."); + assertionError("No terminal events received."); } } + /** + * Combines an assertion error message with the current completion and error state of this + * TestSubscriber, giving more information when some assertXXX check fails. + * @param message the message to use for the error + */ + final void assertionError(String message) { + StringBuilder b = new StringBuilder(); + + if (onCompletedEvents.isEmpty()) { + b.append("(active) "); + } + + b.append(message); + + if (!onErrorEvents.isEmpty()) { + b.append(" (+ ") + .append(onErrorEvents.size()) + .append(" errors)"); + } + + AssertionError ae = new AssertionError(b.toString()); + if (!onErrorEvents.isEmpty()) { + if (onErrorEvents.size() == 1) { + ae.initCause(onErrorEvents.get(0)); + } else { + ae.initCause(new CompositeException(onErrorEvents)); + } + } + throw ae; + } + // do nothing ... including swallowing errors private static Observer INERT = new Observer() { diff --git a/src/main/java/rx/observers/TestSubscriber.java b/src/main/java/rx/observers/TestSubscriber.java index 887bd2998b..5c64b3a8f8 100644 --- a/src/main/java/rx/observers/TestSubscriber.java +++ b/src/main/java/rx/observers/TestSubscriber.java @@ -292,7 +292,7 @@ public void assertTerminalEvent() { */ public void assertUnsubscribed() { if (!isUnsubscribed()) { - throw new AssertionError("Not unsubscribed."); + testObserver.assertionError("Not unsubscribed."); } } @@ -393,10 +393,10 @@ public Thread getLastSeenThread() { public void assertCompleted() { int s = testObserver.getOnCompletedEvents().size(); if (s == 0) { - throw new AssertionError("Not completed!"); + testObserver.assertionError("Not completed!"); } else if (s > 1) { - throw new AssertionError("Completed multiple times: " + s); + testObserver.assertionError("Completed multiple times: " + s); } } @@ -409,10 +409,10 @@ public void assertCompleted() { public void assertNotCompleted() { int s = testObserver.getOnCompletedEvents().size(); if (s == 1) { - throw new AssertionError("Completed!"); + testObserver.assertionError("Completed!"); } else if (s > 1) { - throw new AssertionError("Completed multiple times: " + s); + testObserver.assertionError("Completed multiple times: " + s); } } @@ -427,7 +427,7 @@ public void assertNotCompleted() { public void assertError(Class clazz) { List err = testObserver.getOnErrorEvents(); if (err.size() == 0) { - throw new AssertionError("No errors"); + testObserver.assertionError("No errors"); } else if (err.size() > 1) { AssertionError ae = new AssertionError("Multiple errors: " + err.size()); @@ -452,7 +452,7 @@ public void assertError(Class clazz) { public void assertError(Throwable throwable) { List err = testObserver.getOnErrorEvents(); if (err.size() == 0) { - throw new AssertionError("No errors"); + testObserver.assertionError("No errors"); } else if (err.size() > 1) { AssertionError ae = new AssertionError("Multiple errors: " + err.size()); @@ -477,7 +477,7 @@ public void assertNoTerminalEvent() { int s = testObserver.getOnCompletedEvents().size(); if (err.size() > 0 || s > 0) { if (err.isEmpty()) { - throw new AssertionError("Found " + err.size() + " errors and " + s + " completion events instead of none"); + testObserver.assertionError("Found " + err.size() + " errors and " + s + " completion events instead of none"); } else if (err.size() == 1) { AssertionError ae = new AssertionError("Found " + err.size() + " errors and " + s + " completion events instead of none"); @@ -500,7 +500,7 @@ public void assertNoTerminalEvent() { public void assertNoValues() { int s = testObserver.getOnNextEvents().size(); if (s > 0) { - throw new AssertionError("No onNext events expected yet some received: " + s); + testObserver.assertionError("No onNext events expected yet some received: " + s); } } @@ -514,7 +514,7 @@ public void assertNoValues() { public void assertValueCount(int count) { int s = testObserver.getOnNextEvents().size(); if (s != count) { - throw new AssertionError("Number of onNext events differ; expected: " + count + ", actual: " + s); + testObserver.assertionError("Number of onNext events differ; expected: " + count + ", actual: " + s); } } diff --git a/src/test/java/rx/observers/TestSubscriberTest.java b/src/test/java/rx/observers/TestSubscriberTest.java index 1974a81a2f..9d42fe7f7a 100644 --- a/src/test/java/rx/observers/TestSubscriberTest.java +++ b/src/test/java/rx/observers/TestSubscriberTest.java @@ -18,7 +18,7 @@ import static org.junit.Assert.*; import static org.mockito.Mockito.*; -import java.util.Arrays; +import java.util.*; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -26,8 +26,10 @@ import org.junit.rules.ExpectedException; import org.mockito.InOrder; -import rx.*; +import rx.Observable; +import rx.Observer; import rx.Scheduler.Worker; +import rx.Subscriber; import rx.exceptions.*; import rx.functions.Action0; import rx.schedulers.Schedulers; @@ -608,11 +610,63 @@ public void assertValuesShouldThrowIfNumberOfItemsDoesNotMatch() { ts.assertValues("1", "2"); fail(); } catch (AssertionError expected) { - assertEquals("Number of items does not match. Provided: 2 Actual: 3.\n" + + assertEquals("(active) Number of items does not match. Provided: 2 Actual: 3.\n" + "Provided values: [1, 2]\n" + "Actual values: [a, b, c]", expected.getMessage() ); } } + + @Test + public void assertionFailureGivesActiveDetails() { + TestSubscriber ts = new TestSubscriber(); + + ts.onNext("a"); + ts.onNext("b"); + ts.onNext("c"); + ts.onError(new TestException("forced failure")); + + try { + ts.assertValues("1", "2"); + fail(); + } catch (AssertionError expected) { + assertEquals("(active) Number of items does not match. Provided: 2 Actual: 3.\n" + + "Provided values: [1, 2]\n" + + "Actual values: [a, b, c] (+ 1 errors)", + expected.getMessage() + ); + Throwable ex = expected.getCause(); + assertEquals(TestException.class, ex.getClass()); + assertEquals("forced failure", ex.getMessage()); + } + } + + @Test + public void assertionFailureShowsMultipleErrors() { + TestSubscriber ts = new TestSubscriber(); + + ts.onNext("a"); + ts.onNext("b"); + ts.onNext("c"); + ts.onError(new TestException("forced failure")); + ts.onError(new TestException("forced failure 2")); + + try { + ts.assertValues("1", "2"); + fail(); + } catch (AssertionError expected) { + assertEquals("(active) Number of items does not match. Provided: 2 Actual: 3.\n" + + "Provided values: [1, 2]\n" + + "Actual values: [a, b, c] (+ 2 errors)", + expected.getMessage() + ); + Throwable ex = expected.getCause(); + assertEquals(CompositeException.class, ex.getClass()); + List list = ((CompositeException)ex).getExceptions(); + assertEquals(2, list.size()); + assertEquals("forced failure", list.get(0).getMessage()); + assertEquals("forced failure 2", list.get(1).getMessage()); + } + } } From e574d42efb38ccd25c8cbc678e1cdcac701a3d69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1vid=20Karnok?= Date: Thu, 12 May 2016 23:01:42 +0200 Subject: [PATCH 2/4] Updated message pattern --- src/main/java/rx/observers/TestObserver.java | 13 +++-- .../java/rx/observers/TestSubscriberTest.java | 56 +++++++++++++++++-- 2 files changed, 57 insertions(+), 12 deletions(-) diff --git a/src/main/java/rx/observers/TestObserver.java b/src/main/java/rx/observers/TestObserver.java index 1f806910c6..760a8c6540 100644 --- a/src/main/java/rx/observers/TestObserver.java +++ b/src/main/java/rx/observers/TestObserver.java @@ -167,16 +167,17 @@ public void assertTerminalEvent() { * @param message the message to use for the error */ final void assertionError(String message) { - StringBuilder b = new StringBuilder(); - - if (onCompletedEvents.isEmpty()) { - b.append("(active) "); - } + StringBuilder b = new StringBuilder(message.length() + 32); b.append(message); + + b.append(" ("); + b.append(onCompletedEvents.size()); + b.append(" completions)"); + if (!onErrorEvents.isEmpty()) { - b.append(" (+ ") + b.append(" (+") .append(onErrorEvents.size()) .append(" errors)"); } diff --git a/src/test/java/rx/observers/TestSubscriberTest.java b/src/test/java/rx/observers/TestSubscriberTest.java index 9d42fe7f7a..be78e8a07b 100644 --- a/src/test/java/rx/observers/TestSubscriberTest.java +++ b/src/test/java/rx/observers/TestSubscriberTest.java @@ -610,9 +610,9 @@ public void assertValuesShouldThrowIfNumberOfItemsDoesNotMatch() { ts.assertValues("1", "2"); fail(); } catch (AssertionError expected) { - assertEquals("(active) Number of items does not match. Provided: 2 Actual: 3.\n" + + assertEquals("Number of items does not match. Provided: 2 Actual: 3.\n" + "Provided values: [1, 2]\n" + - "Actual values: [a, b, c]", + "Actual values: [a, b, c] (0 completions)", expected.getMessage() ); } @@ -631,9 +631,9 @@ public void assertionFailureGivesActiveDetails() { ts.assertValues("1", "2"); fail(); } catch (AssertionError expected) { - assertEquals("(active) Number of items does not match. Provided: 2 Actual: 3.\n" + + assertEquals("Number of items does not match. Provided: 2 Actual: 3.\n" + "Provided values: [1, 2]\n" + - "Actual values: [a, b, c] (+ 1 errors)", + "Actual values: [a, b, c] (0 completions) (+1 errors)", expected.getMessage() ); Throwable ex = expected.getCause(); @@ -656,9 +656,9 @@ public void assertionFailureShowsMultipleErrors() { ts.assertValues("1", "2"); fail(); } catch (AssertionError expected) { - assertEquals("(active) Number of items does not match. Provided: 2 Actual: 3.\n" + + assertEquals("Number of items does not match. Provided: 2 Actual: 3.\n" + "Provided values: [1, 2]\n" + - "Actual values: [a, b, c] (+ 2 errors)", + "Actual values: [a, b, c] (0 completions) (+2 errors)", expected.getMessage() ); Throwable ex = expected.getCause(); @@ -669,4 +669,48 @@ public void assertionFailureShowsMultipleErrors() { assertEquals("forced failure 2", list.get(1).getMessage()); } } + + @Test + public void assertionFailureShowsCompletion() { + TestSubscriber ts = new TestSubscriber(); + + ts.onNext("a"); + ts.onNext("b"); + ts.onNext("c"); + ts.onCompleted(); + + try { + ts.assertValues("1", "2"); + fail(); + } catch (AssertionError expected) { + assertEquals("Number of items does not match. Provided: 2 Actual: 3.\n" + + "Provided values: [1, 2]\n" + + "Actual values: [a, b, c] (1 completions)", + expected.getMessage() + ); + } + } + + @Test + public void assertionFailureShowsMultipleCompletions() { + TestSubscriber ts = new TestSubscriber(); + + ts.onNext("a"); + ts.onNext("b"); + ts.onNext("c"); + ts.onCompleted(); + ts.onCompleted(); + + try { + ts.assertValues("1", "2"); + fail(); + } catch (AssertionError expected) { + assertEquals("Number of items does not match. Provided: 2 Actual: 3.\n" + + "Provided values: [1, 2]\n" + + "Actual values: [a, b, c] (2 completions)", + expected.getMessage() + ); + } + } + } From 400eca66f3d774783ee18a1ad7f43be0c2dd73a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1vid=20Karnok?= Date: Thu, 12 May 2016 23:05:19 +0200 Subject: [PATCH 3/4] Newline after assertReceivedOnNext() message --- src/main/java/rx/observers/TestObserver.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/rx/observers/TestObserver.java b/src/main/java/rx/observers/TestObserver.java index 760a8c6540..461e905366 100644 --- a/src/main/java/rx/observers/TestObserver.java +++ b/src/main/java/rx/observers/TestObserver.java @@ -116,7 +116,8 @@ public void assertReceivedOnNext(List items) { + ".\n" + "Provided values: " + items + "\n" - + "Actual values: " + onNextEvents); + + "Actual values: " + onNextEvents + + "\n"); } for (int i = 0; i < items.size(); i++) { @@ -125,12 +126,12 @@ public void assertReceivedOnNext(List items) { if (expected == null) { // check for null equality if (actual != null) { - assertionError("Value at index: " + i + " expected to be [null] but was: [" + actual + "]"); + assertionError("Value at index: " + i + " expected to be [null] but was: [" + actual + "]\n"); } } else if (!expected.equals(actual)) { assertionError("Value at index: " + i + " expected to be [" + expected + "] (" + expected.getClass().getSimpleName() - + ") but was: [" + actual + "] (" + (actual != null ? actual.getClass().getSimpleName() : "null") + ")"); + + ") but was: [" + actual + "] (" + (actual != null ? actual.getClass().getSimpleName() : "null") + ")\n"); } } From 0a273fbd46a0a2d27debbada4d709b5fc9747a6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1vid=20Karnok?= Date: Thu, 12 May 2016 23:09:20 +0200 Subject: [PATCH 4/4] Add plural/singular form --- src/main/java/rx/observers/TestObserver.java | 18 ++++++++++++++---- .../java/rx/observers/TestSubscriberTest.java | 10 +++++----- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/main/java/rx/observers/TestObserver.java b/src/main/java/rx/observers/TestObserver.java index 461e905366..45d89dae7c 100644 --- a/src/main/java/rx/observers/TestObserver.java +++ b/src/main/java/rx/observers/TestObserver.java @@ -174,13 +174,23 @@ final void assertionError(String message) { b.append(" ("); - b.append(onCompletedEvents.size()); - b.append(" completions)"); + int c = onCompletedEvents.size(); + b.append(c); + b.append(" completion"); + if (c != 1) { + b.append("s"); + } + b.append(")"); if (!onErrorEvents.isEmpty()) { + int size = onErrorEvents.size(); b.append(" (+") - .append(onErrorEvents.size()) - .append(" errors)"); + .append(size) + .append(" error"); + if (size != 1) { + b.append("s"); + } + b.append(")"); } AssertionError ae = new AssertionError(b.toString()); diff --git a/src/test/java/rx/observers/TestSubscriberTest.java b/src/test/java/rx/observers/TestSubscriberTest.java index be78e8a07b..9ff09edd20 100644 --- a/src/test/java/rx/observers/TestSubscriberTest.java +++ b/src/test/java/rx/observers/TestSubscriberTest.java @@ -612,7 +612,7 @@ public void assertValuesShouldThrowIfNumberOfItemsDoesNotMatch() { } catch (AssertionError expected) { assertEquals("Number of items does not match. Provided: 2 Actual: 3.\n" + "Provided values: [1, 2]\n" + - "Actual values: [a, b, c] (0 completions)", + "Actual values: [a, b, c]\n (0 completions)", expected.getMessage() ); } @@ -633,7 +633,7 @@ public void assertionFailureGivesActiveDetails() { } catch (AssertionError expected) { assertEquals("Number of items does not match. Provided: 2 Actual: 3.\n" + "Provided values: [1, 2]\n" + - "Actual values: [a, b, c] (0 completions) (+1 errors)", + "Actual values: [a, b, c]\n (0 completions) (+1 error)", expected.getMessage() ); Throwable ex = expected.getCause(); @@ -658,7 +658,7 @@ public void assertionFailureShowsMultipleErrors() { } catch (AssertionError expected) { assertEquals("Number of items does not match. Provided: 2 Actual: 3.\n" + "Provided values: [1, 2]\n" + - "Actual values: [a, b, c] (0 completions) (+2 errors)", + "Actual values: [a, b, c]\n (0 completions) (+2 errors)", expected.getMessage() ); Throwable ex = expected.getCause(); @@ -685,7 +685,7 @@ public void assertionFailureShowsCompletion() { } catch (AssertionError expected) { assertEquals("Number of items does not match. Provided: 2 Actual: 3.\n" + "Provided values: [1, 2]\n" + - "Actual values: [a, b, c] (1 completions)", + "Actual values: [a, b, c]\n (1 completion)", expected.getMessage() ); } @@ -707,7 +707,7 @@ public void assertionFailureShowsMultipleCompletions() { } catch (AssertionError expected) { assertEquals("Number of items does not match. Provided: 2 Actual: 3.\n" + "Provided values: [1, 2]\n" + - "Actual values: [a, b, c] (2 completions)", + "Actual values: [a, b, c]\n (2 completions)", expected.getMessage() ); }