Skip to content

Commit 6d9d540

Browse files
TestObserver
1 parent 5478e30 commit 6d9d540

File tree

5 files changed

+288
-1
lines changed

5 files changed

+288
-1
lines changed

rxjava-core/src/main/java/rx/Notification.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,35 @@ public class Notification<T> {
2626
private final Throwable throwable;
2727
private final T value;
2828

29+
public static <T> Notification<T> createOnNext(T t) {
30+
return new Notification<T>(Kind.OnNext, t, null);
31+
}
32+
33+
public static <T> Notification<T> createOnError(Throwable e) {
34+
return new Notification<T>(Kind.OnError, null, e);
35+
}
36+
37+
public static <T> Notification<T> createOnCompleted() {
38+
return new Notification<T>(Kind.OnCompleted, null, null);
39+
}
40+
41+
public static <T> Notification<T> createOnCompleted(Class<T> type) {
42+
return new Notification<T>(Kind.OnCompleted, null, null);
43+
}
44+
45+
private Notification(Kind kind, T value, Throwable e) {
46+
this.value = value;
47+
this.throwable = e;
48+
this.kind = kind;
49+
}
50+
2951
/**
3052
* A constructor used to represent an onNext notification.
3153
*
3254
* @param value
3355
* The data passed to the onNext method.
3456
*/
57+
@Deprecated
3558
public Notification(T value) {
3659
this.value = value;
3760
this.throwable = null;
@@ -43,7 +66,9 @@ public Notification(T value) {
4366
*
4467
* @param exception
4568
* The exception passed to the onError notification.
69+
* @deprecated Because type Throwable can't disambiguate the constructors if both onNext and onError are type "Throwable"
4670
*/
71+
@Deprecated
4772
public Notification(Throwable exception) {
4873
this.throwable = exception;
4974
this.value = null;
@@ -53,6 +78,7 @@ public Notification(Throwable exception) {
5378
/**
5479
* A constructor used to represent an onCompleted notification.
5580
*/
81+
@Deprecated
5682
public Notification() {
5783
this.throwable = null;
5884
this.value = null;

rxjava-core/src/main/java/rx/Observer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ protected Observer(Observer<?> op) {
7272
*
7373
* @param args
7474
*/
75-
public abstract void onNext(T args);
75+
public abstract void onNext(T t);
7676

7777
/**
7878
* Used to register an unsubscribe callback.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package rx.observers;
2+
3+
import rx.Observer;
4+
5+
/**
6+
* Observer that does nothing... including swallowing errors.
7+
*/
8+
public class EmptyObserver<T> extends Observer<T> {
9+
10+
@Override
11+
public void onCompleted() {
12+
13+
}
14+
15+
@Override
16+
public void onError(Throwable e) {
17+
18+
}
19+
20+
@Override
21+
public void onNext(T args) {
22+
23+
}
24+
25+
}
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
/**
2+
* Copyright 2014 Netflix, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package rx.observers;
17+
18+
import java.util.ArrayList;
19+
import java.util.Collections;
20+
import java.util.List;
21+
22+
import rx.Notification;
23+
import rx.Observer;
24+
25+
/**
26+
* Observer usable for unit testing to perform assertions, inspect received events or wrap a mocked Observer.
27+
*/
28+
public class TestObserver<T> extends Observer<T> {
29+
30+
private final Observer<Object> EMPTY = new EmptyObserver<Object>();
31+
32+
private final Observer<T> delegate;
33+
private final ArrayList<T> onNextEvents = new ArrayList<T>();
34+
private final ArrayList<Throwable> onErrorEvents = new ArrayList<Throwable>();
35+
private final ArrayList<Notification<T>> onCompletedEvents = new ArrayList<Notification<T>>();
36+
37+
public TestObserver(Observer<T> delegate) {
38+
this.delegate = delegate;
39+
}
40+
41+
@SuppressWarnings("unchecked")
42+
public TestObserver() {
43+
this.delegate = (Observer<T>) EMPTY;
44+
}
45+
46+
@Override
47+
public void onCompleted() {
48+
onCompletedEvents.add(Notification.<T> createOnCompleted());
49+
delegate.onCompleted();
50+
}
51+
52+
public List<Notification<T>> getOnCompletedEvents() {
53+
return Collections.unmodifiableList(onCompletedEvents);
54+
}
55+
56+
@Override
57+
public void onError(Throwable e) {
58+
onErrorEvents.add(e);
59+
delegate.onError(e);
60+
}
61+
62+
public List<Throwable> getOnErrorEvents() {
63+
return Collections.unmodifiableList(onErrorEvents);
64+
}
65+
66+
@Override
67+
public void onNext(T t) {
68+
onNextEvents.add(t);
69+
delegate.onNext(t);
70+
}
71+
72+
public List<T> getOnNextEvents() {
73+
return Collections.unmodifiableList(onNextEvents);
74+
}
75+
76+
public void assertReceivedOnNext(List<T> items) {
77+
if (onNextEvents.size() != items.size()) {
78+
throw new AssertionError("Number of items does not match. Provided: " + items.size() + " Actual: " + onNextEvents.size());
79+
}
80+
81+
for (int i = 0; i < items.size(); i++) {
82+
if (items.get(i) == null) {
83+
// check for null equality
84+
if (onNextEvents.get(i) != null) {
85+
throw new AssertionError("Value at index: " + i + " expected to be [null] but was: [" + onNextEvents.get(i) + "]");
86+
}
87+
} else if (!items.get(i).equals(onNextEvents.get(i))) {
88+
throw new AssertionError("Value at index: " + i + " expected to be [" + items.get(i) + "] but was: [" + onNextEvents.get(i) + "]");
89+
}
90+
}
91+
92+
}
93+
94+
/**
95+
* Assert that a single terminal event occurred, either onCompleted or onError.
96+
*/
97+
public void assertTerminalEvent() {
98+
if (onErrorEvents.size() > 1) {
99+
throw new AssertionError("Too many onError events: " + onErrorEvents.size());
100+
}
101+
102+
if (onCompletedEvents.size() > 1) {
103+
throw new AssertionError("Too many onCompleted events: " + onCompletedEvents.size());
104+
}
105+
106+
if (onCompletedEvents.size() == 1 && onErrorEvents.size() == 1) {
107+
throw new AssertionError("Received both an onError and onCompleted. Should be one or the other.");
108+
}
109+
110+
if (onCompletedEvents.size() == 0 && onErrorEvents.size() == 0) {
111+
throw new AssertionError("No terminal events received.");
112+
}
113+
}
114+
115+
}
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
/**
2+
* Copyright 2014 Netflix, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package rx.observers;
17+
18+
import static org.junit.Assert.*;
19+
import static org.mockito.Mockito.*;
20+
21+
import java.util.Arrays;
22+
23+
import org.junit.Rule;
24+
import org.junit.Test;
25+
import org.junit.rules.ExpectedException;
26+
import org.mockito.InOrder;
27+
28+
import rx.Observable;
29+
import rx.Observer;
30+
import rx.subjects.PublishSubject;
31+
32+
public class TestObserverTest {
33+
34+
@Rule
35+
public ExpectedException thrown = ExpectedException.none();
36+
37+
@Test
38+
public void testAssert() {
39+
Observable<Integer> oi = Observable.from(Arrays.asList(1, 2));
40+
TestObserver<Integer> o = new TestObserver<Integer>();
41+
oi.subscribe(o);
42+
43+
o.assertReceivedOnNext(Arrays.asList(1, 2));
44+
assertEquals(2, o.getOnNextEvents().size());
45+
o.assertTerminalEvent();
46+
}
47+
48+
@Test
49+
public void testAssertNotMatchCount() {
50+
Observable<Integer> oi = Observable.from(Arrays.asList(1, 2));
51+
TestObserver<Integer> o = new TestObserver<Integer>();
52+
oi.subscribe(o);
53+
54+
thrown.expect(AssertionError.class);
55+
thrown.expectMessage("Number of items does not match. Provided: 1 Actual: 2");
56+
57+
o.assertReceivedOnNext(Arrays.asList(1));
58+
assertEquals(2, o.getOnNextEvents().size());
59+
o.assertTerminalEvent();
60+
}
61+
62+
@Test
63+
public void testAssertNotMatchValue() {
64+
Observable<Integer> oi = Observable.from(Arrays.asList(1, 2));
65+
TestObserver<Integer> o = new TestObserver<Integer>();
66+
oi.subscribe(o);
67+
68+
thrown.expect(AssertionError.class);
69+
thrown.expectMessage("Value at index: 1 expected to be [3] but was: [2]");
70+
71+
o.assertReceivedOnNext(Arrays.asList(1, 3));
72+
assertEquals(2, o.getOnNextEvents().size());
73+
o.assertTerminalEvent();
74+
}
75+
76+
@Test
77+
public void testAssertTerminalEventNotReceived() {
78+
PublishSubject<Integer> p = PublishSubject.create();
79+
TestObserver<Integer> o = new TestObserver<Integer>();
80+
p.toObservable().subscribe(o);
81+
82+
p.onNext(1);
83+
p.onNext(2);
84+
85+
thrown.expect(AssertionError.class);
86+
thrown.expectMessage("No terminal events received.");
87+
88+
o.assertReceivedOnNext(Arrays.asList(1, 2));
89+
assertEquals(2, o.getOnNextEvents().size());
90+
o.assertTerminalEvent();
91+
}
92+
93+
@Test
94+
public void testWrappingMock() {
95+
Observable<Integer> oi = Observable.from(Arrays.asList(1, 2));
96+
@SuppressWarnings("unchecked")
97+
Observer<Integer> mockObserver = mock(Observer.class);
98+
oi.subscribe(new TestObserver<Integer>(mockObserver));
99+
100+
InOrder inOrder = inOrder(mockObserver);
101+
inOrder.verify(mockObserver, times(1)).onNext(1);
102+
inOrder.verify(mockObserver, times(1)).onNext(2);
103+
inOrder.verify(mockObserver, times(1)).onCompleted();
104+
inOrder.verifyNoMoreInteractions();
105+
}
106+
107+
@Test
108+
public void testWrappingMockWhenUnsubscribeInvolved() {
109+
Observable<Integer> oi = Observable.from(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9)).take(2);
110+
@SuppressWarnings("unchecked")
111+
Observer<Integer> mockObserver = mock(Observer.class);
112+
oi.subscribe(new TestObserver<Integer>(mockObserver));
113+
114+
InOrder inOrder = inOrder(mockObserver);
115+
inOrder.verify(mockObserver, times(1)).onNext(1);
116+
inOrder.verify(mockObserver, times(1)).onNext(2);
117+
inOrder.verify(mockObserver, times(1)).onCompleted();
118+
inOrder.verifyNoMoreInteractions();
119+
}
120+
121+
}

0 commit comments

Comments
 (0)