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

1.x: support Observable.test() #4777

Merged
merged 1 commit into from
Nov 9, 2016

Conversation

davidmoten
Copy link
Collaborator

@davidmoten davidmoten commented Oct 28, 2016

The new method chaining TestSubscriber in 2.x is too good not to have in 1.x as well (while I wait for 2.x to mature and to make my existing unit tests less verbose).

Observable
  .just(1)
  .test()
  .assertValue(1)
  .assertCompleted();

Observable.test() returns a TestSubscriber2 that wraps a TestSubscriber and enables method chaining where it can.

I've added tests to get to 100% test coverage though I've been pretty slap-dash about testing some of the details (for example I haven't tested that awaitXXX really does wait for stuff).

I've noted it as @since 1.2.3 but can update to 1.2.2 if you are happy to squeak it into the release.

@codecov-io
Copy link

codecov-io commented Oct 28, 2016

Current coverage is 84.08% (diff: 100%)

Merging #4777 into 1.x will decrease coverage by 0.08%

@@                1.x      #4777   diff @@
==========================================
  Files           286        287     +1   
  Lines         17754      17828    +74   
  Methods           0          0          
  Messages          0          0          
  Branches       2702       2702          
==========================================
+ Hits          14943      14990    +47   
- Misses         1952       1967    +15   
- Partials        859        871    +12   

Powered by Codecov. Last update 6385051...ef91729

@akarnokd
Copy link
Member

I have mixed feelings about this. 2.x required massive amount of unit tests, often with the same structure across the base types where this was a valuable addition. Maybe you could flesh it out in rxjava-extras and provide a function for the to operator:

import static TestConsumer.*;

Observable.just(1)
.to(newTest(0L))
.assertEmpty()
.requestMore(1)
.assertResult(1);

You can base it on Subscriber and implement CompletableSubscriber but I wish we introduced a base interface for SingleSubscriber before going 1.2.

@MyDogTom
Copy link

MyDogTom commented Oct 28, 2016

Just for reference. There are at least two libraries that aim to provide similar functionality (chaining assertions):

  • RxTestWrapper - just wraps TestSubsriber in a similar manner as this PR
  • rxassertions - provides additional assertions, but also introduce some limitations.

@davidmoten
Copy link
Collaborator Author

davidmoten commented Oct 28, 2016

@akarnokd, @MyDogTom
I have been using this functionality in rxjava-extras using .to(TestingHelper.test()) for about five weeks. It just seems too useful to miss out on natively (don't have to add extra library to pom.xml, add import, put up with slightly less readable line in code). Discoverability is sorted for new users too.

For anyone writing tests for their observables I think this is very attractive functionality to have natively and I was stoked to use it with rxjava 2.

Note that my motivation for this is for writing tests in my projects (not rxjava 1.x core, though that would be nice too).

I forgot to consider Completable and Single because I don't use them (the majority of our rxjava based code predates their existence). Do they need special treatment (other than addition of a test method there)?

@akarnokd
Copy link
Member

How about having an interface with the get and assert methods and the 3 test()s return it. In an internal package then you can implement them and have delegates as necessary.

@davidmoten
Copy link
Collaborator Author

How about having an interface with the get and assert methods and the 3 test()s return it. In an internal package then you can implement them and have delegates as necessary.

Yep I can do that.

@davidmoten
Copy link
Collaborator Author

How about having an interface with the get and assert methods and the 3 test()s return it. In an internal package then you can implement them and have delegates as necessary.

Having extracted an interface the one implementation can serve all test() methods by subscribing to Completable, Single or Observable instances using subscribe(Subscriber). How do you want the implementations to differ?

@@ -16,19 +16,48 @@

package rx;

import java.util.*;
import java.util.concurrent.*;
import java.util.Arrays;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please make sure you don't unroll * imports.

ts.assertValue(value);
return this;
}
public interface TestSubscriber2<T> extends Observer<T> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd call this TestConsumer because that 2 at the end will make people wonder and question endlessly.


}
boolean isUnsubscribed();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reinforce with implements Subscription?

@MyDogTom
Copy link

MyDogTom commented Nov 2, 2016

I think test() should be added also for BlockingObservable.

@akarnokd
Copy link
Member

akarnokd commented Nov 2, 2016

Unnecessary. TestConsumer should have awaitTerminalEvent that blocks.

@davidmoten
Copy link
Collaborator Author

TestConsumer maybe not ideal because sounds like it's a significantly different beast from a TestSubscriber. I prefer to retain the word Subscriber in the name, what about TestSubscriberFluent?

@akarnokd
Copy link
Member

akarnokd commented Nov 4, 2016

AssertableSubscriber

@MyDogTom
Copy link

MyDogTom commented Nov 6, 2016

+1 for TestSubscriberFluent because it reflects the main difference from TestSubscriber - fluent API
-1 for AssertableSubscriber it doesn't provide any additional info (TestSubscriber is also "assertable").

@akarnokd I still think that BlockingObservable should have test() method. Let's assume that I encounter some legacy code that use BlockingObservable (regardless it's not recommended). I would like to be able write test first and refactor it afterwards (get rid of BlockingObservable).

@davidmoten davidmoten force-pushed the chained-test-subscriber branch from e47b1ef to a789ebd Compare November 7, 2016 09:45
@davidmoten
Copy link
Collaborator Author

@MyDogTom puts it well I think, after all AssertableSubscriber would provoke as many "what's the difference?" questions as TestSubscriber2. TestSubscriberFluent describes exactly what the class is.

@davidmoten
Copy link
Collaborator Author

fixed imports, renamed TestSubscriber2 to TestSubscriberFluent (though I'm not assuming the naming discussion has finished).

squashed commits and rebased on latest 1.x

@akarnokd
Copy link
Member

akarnokd commented Nov 7, 2016

AssertableSubscriber should be an interface with all the relevant assert methods that any actual internal test class can implement on top of the respective receiver type.

I think, after all AssertableSubscriber would provoke as many "what's the difference?"

If this comes up we will answer that it is the base API interface for asserting a subscriber's state for all 3 reactive types.

I see confusion going the other way around: why TestSubscriber2 or TestSubscriberFluent; its prefix TestSubscriber matches, well, TestSubscriber itself.

@davidmoten davidmoten force-pushed the chained-test-subscriber branch from 00c2467 to 9bed046 Compare November 8, 2016 18:41
@davidmoten
Copy link
Collaborator Author

Righto, let's go with AssertableSubscriber. Modified code and squashed commits

* @since 1.2.3
*/
@Experimental
public final AssertableSubscriber<Void> test(long initialRequestAmount) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Completable doesn't need backpressure so this method has no use.

* @since 1.2.3
*/
@Experimental
public final AssertableSubscriber<T> test(long initialRequestAmount) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Single doesn't support backpressure either.

@davidmoten davidmoten force-pushed the chained-test-subscriber branch from 9bed046 to 19bbbfa Compare November 9, 2016 02:42
@davidmoten davidmoten force-pushed the chained-test-subscriber branch from 19bbbfa to ef91729 Compare November 9, 2016 02:46
@davidmoten
Copy link
Collaborator Author

Thanks @akarnokd, made fixes and squashed commits.

@akarnokd akarnokd added this to the 1.3 milestone Nov 9, 2016
@akarnokd akarnokd merged commit d6bc923 into ReactiveX:1.x Nov 9, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants