Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use TestScheduler in RxJava

Tags:

How should I use RxJava's TestScheduler? I come from a .NET background but the TestScheduler in RxJava does not seem to work the same way as the test scheduler in .NET rx.

Here is sample code that I want to test

Observable<Long> tick = Observable.interval(1, TimeUnit.SECONDS);
contactsRepository.find(index)
  .buffer(MAX_CONTACTS_FETCH)
  .zipWith(tick, new Func2<List<ContactDto>, Long, List<ContactDto>>() {
    @Override
    public List<ContactDto> call(List<ContactDto> contactList, Long aLong) {
      return contactList;
    }
  }).subscribe()

I've tried:

subscribeOn(testScheduler)
testScheduler.advanceTimeBy(2, TimeUnit.SECONDS);
testScheduler.triggerActions();

with no luck.

like image 575
Calin Avatar asked Nov 02 '14 12:11

Calin


2 Answers

I made a little example of how to use a TestScheduler. I think it's very similar to the .NET implementation

@Test
public void should_test_the_test_schedulers() {
    TestScheduler scheduler = new TestScheduler();
    final List<Long> result = new ArrayList<>();
    Observable.interval(1, TimeUnit.SECONDS, scheduler)
        .take(5)
        .subscribe(result::add);
    assertTrue(result.isEmpty());
    scheduler.advanceTimeBy(2, TimeUnit.SECONDS);
    assertEquals(2, result.size());
    scheduler.advanceTimeBy(10, TimeUnit.SECONDS);
    assertEquals(5, result.size());
}

https://github.com/bric3/demo-rxjava-humantalk/blob/master/src/test/java/demo/humantalk/rxjava/SchedulersTest.java

EDIT According to your code : you should pass the scheduler to the Observable.interval operation, as this is what you want to control :

    TestScheduler scheduler = new TestScheduler();

    Observable<Long> tick = Observable.interval(1, TimeUnit.SECONDS, scheduler);
    Subscription toBeTested = Observable.from(Arrays.asList(1, 2, 3, 4, 5))
            .buffer(3)
            .zipWith(tick, (i, t) -> i)
            .subscribe(System.out::println);

    scheduler.advanceTimeBy(2, TimeUnit.SECONDS);
like image 157
dwursteisen Avatar answered Oct 05 '22 02:10

dwursteisen


you have some class:

public class SomeClass {
  public void someMethod() {
    Observable<Long> tick = Observable.interval(1, TimeUnit.SECONDS);
    contactsRepository.find(index)
      .buffer(MAX_CONTACTS_FETCH)
      .zipWith(tick, new Func2<List<ContactDto>, Long, List<ContactDto>>() {
        @Override
        public List<ContactDto> call(List<ContactDto> contactList, Long aLong) {
          return contactList;
        }
      }).subscribe()
  }
}

Look up [Observable.interval][1] in the docs and you will see it operates on the computation scheduler, so lets override that in our test.

public class SomeClassTest {
  private TestScheduler testScheduler;

  @Before
  public void before() {
    testScheduler = new TestScheduler();
    // set calls to Schedulers.computation() to use our test scheduler
    RxJavaPlugins.setComputationSchedulerHandler(ignore -> testScheduler);
  }

  @After
  public void after() {
    // reset it
    RxJavaPlugins.setComputationSchedulerHandler(null);
  }

  @Test
  public void test() {
    SomeClass someInstance = new SomeClass();
    someInstance.someMethod();

    // advance time manually
    testScheduler.advanceBy(1, TimeUnit.SECONDS);
  }

This solution is an improvement to the accepted answer as the quality, integrity and simplicity of the production code is maintained.

like image 27
Andrew Gallasch Avatar answered Oct 05 '22 02:10

Andrew Gallasch