Testing asynchronous Observable instances with the help of the TestScheduler class

There is one last type of predefined scheduler that we didn't mention in Chapter 6, Using Concurrency and Parallelism with Schedulers. This is the TestScheduler scheduler, a scheduler designed to be used in unit tests. All the actions scheduled on it are wrapped in objects containing the time they should be executed at, and won't be executed before the triggerActions() method of the Scheduler instance is called. This method executes all of the actions that are not executed and are scheduled to be executed at or before the Scheduler instance's present time. This time is virtual. This means that it is set by us and we can advance to any moment in the future using the special methods of this scheduler.

In order to demonstrate it, we'll want to develop another method for creating a new type of observable. The implementation of the method itself won't be discussed in this chapter, but you can find it in the source code accompanying the book.

The method creates an Observable instance emitting items at set time intervals. But the intervals are not equally spaced, such as with the built-in interval method. The idea is that we can provide a list of different multiple intervals and the Observable instance will cycle through it infinitely. The signature of the method is as follows:

Observable<Long> interval(List<Long> gaps, TimeUnit unit, Scheduler scheduler)

Its behavior should be the same as that of the Observable.interval method if we pass a List variable containing only one time period value. And here is the test for this case:

@Test
public void testBehavesAsNormalIntervalWithOneGap() {
  TestScheduler testScheduler = Schedulers.test(); // (1)
  Observable<Long> interval = CreateObservable.interval(
    Arrays.asList(100L), TimeUnit.MILLISECONDS, testScheduler
  ); // (2)
  TestSubscriber<Long> subscriber = new TestSubscriber<Long>();
  interval.subscribe(subscriber); // (3)
  assertTrue(subscriber.getOnNextEvents().isEmpty()); // (4)
  testScheduler.advanceTimeBy(101L, TimeUnit.MILLISECONDS); // (5)
  assertEquals(Arrays.asList(0L), subscriber.getOnNextEvents());
  testScheduler.advanceTimeBy(101L, TimeUnit.MILLISECONDS); // (6)
  assertEquals(
    Arrays.asList(0L, 1L),
    subscriber.getOnNextEvents()
  );
  testScheduler.advanceTimeTo(1L, TimeUnit.SECONDS); // (7)
  assertEquals(
    Arrays.asList(0L, 1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L),
    subscriber.getOnNextEvents()
  );
}

Let's take a look at the following explaination:

  1. We create the TestScheduler instance, using the Schedulers.test() method.
  2. Our method receives a Scheduler instance as its third parameter. It will emit items on it, so we pass our TestScheduler instance.
  3. Using a TestSubscriber instance, we subscribe to the Observable instance.
  4. Immediately after subscribing, we shouldn't have any notifications, so we check that.
  5. The TestScheduler instance has an advanceTimeBy(long, TimeUnit) method, which controls the time of its Worker instances, so we can use it to get 101 milliseconds into the future. After 101 milliseconds, we expect to have received one item—0.
  6. Using the advanceTimeBy() method, we advance 101 more milliseconds into the future, and we should have received 0 and 1.
  7. The other important method of the TestScheduler instance is the advanceTimeTo(long, TimeUnit) method. It can be used to advance to a specific time point in the future. So we use it to get to the moment when exactly one second from the subscription has passed. We expect to have received ten notifications by that time.

The TestScheduler instance controls the time using its advanceTimeBy()and advanceTimeTo() methods, so we don't need to block the main Thread instance waiting for something to happen. We can just go to the time it has already happened. With the TestScheduler instance, there is a global order of events. So, if two tasks are scheduled for the exact same time, they have an order in which they will execute and can cause problems with the test that expect a specific global order. If we have such an operator to test, we should avoid this by timing to different values—one to 100 ms and the other to 101 ms. Using this technique, testing asynchronous Observable instances is not such a complex task anymore.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.15.137.59