TABLE OF CONTENT
Hi,
In the previous post I told you how to implement the Reactive Api client with caching. Well, to prove that our code works we might want to have some unit tests.
To do so let’s create a new project (Class Library), add a reference to the main project, and install few NuGet packages.
Rx-Main, Rx-Testing, xUnit.Net and Moq.
Install-Package Rx-Main
Install-Package Rx-Testing
Install-Package xunit
Install-Package Moq
Then weāll create a class RxGitHubClientTest, inherited from ReactiveTest.
ReactiveTest
is a base class which is shipped with Rx-Testing
. It implements some infrastructure for our tests.
Iām not going to post all the tests here, you can find them on GitHub (the link is in the end of the post).
Setup #
As an example Iām going to test this scenario: If the cache is empty GitHubClient should download them, persist in a cache, and call OnNext
and OnCompleted
.
To do that weāll need to mock IHttpClient
and IRatingCache
. Another class that weāll use is TestScheduler from Rx-Test
package.
TestScheduler implements IScheduler interface and can be substituted instead of platform dependent instance of scheduler. It allows us literally to control the time and execute asynchronous code step by step. If youād like to know more details I recommend to read a good post āTesting Rx Queries using Virtual Time Schedulingā.
Now our test set up looks like this:
public RxGitHubClientTest()
{
Model = new RatingModel
{
Rating = 10,
LastModified = new DateTime(2015, 07, 10, 1, 1, 1, 0),
Id = UserName
};
Cache = new Mock<IRatingCache>();
Scheduler = new TestScheduler();
HttpClient = new Mock<IHttpClient>();
}
Arrange #
First of all we need to set up Mocks behavior: the cache is empty, http client can download and parse the request.
Cache.Setup(c => c.HasCached(It.IsAny<string>()))
.Returns(false);
Cache.Setup(cache => cache.GetCachedItem(UserName))
.Returns((RatingModel) null);
HttpClient.Setup(http => http.Get(UserName))
.ReturnsAsync(new RatingResponse
{
Data = Model,
IsSuccessful = true
});
var client = CreateClient();
OnNext
and OnCompleted
events.
var expected = Scheduler
.CreateHotObservable(OnNext(2, Model), OnCompleted<RatingModel>(2));
OnNext(2, Model)
is a method which is declared in ReactiveTest
.
It has a following signature:
static Recorded<Notification<T>> OnNext<T>(long ticks, T value)
OnNext
method was called with Model parameter. The Magic Number “2” is nothing more than the time in ticks. Itās not a real time, itās the TestSchedulerās virtual time. It’s not the most elegant solution though. The TestScheduler is created at tick number zero, on the tick number one we subscribe on events and on the tick two weāll get the first event.
Act #
var results = Scheduler
.Start(() => client.GetRatingForUser(UserName), 0, 1, 10);
TestScheduler
, which will be initialized in the zero tick, then it will subscribe on client.GetRatingForUser(UserName)
on the first tick. The last parameter is the virtual time on which the subscriber should be disposed. We can ignore this one for now.
And finally the last step.
Assert #
ReactiveAssert.AreElementsEqual(expected.Messages, results.Messages);
Cache.Verify(cache => cache.Put(Model), Times.Once);
This test could have been splitted into two parts, to follow the best practices (to have one assert per test method), but I wanted to show that all the regular approaches are valid in Rx-world.
Ok, now we have a green bar.
Source code #
You can find the source code on GitHub
Conclusion #
I mentioned Windows Phone development in my article, however the code is built for .NET 4.5. I did that on purpose, to let everybody checkout and build the solution even if one has no WP SDK installed locally. Itās easy to convert the project to PCL or WP8/8.1 assembly. All of them are supported by Rx.