Skip to content

How to replace TestScheduler in RxJavaSchedulersHook? #3914

@DavidMihola

Description

@DavidMihola

So, I am currently trying to test some time sensitive code in my Android app:

  • Inside my MainPresenter I use interval() and delay() to do a countdown and display text in the View accordingly. Both, by default, use Schedulers.computation().
  • For testing, I'd like to use a TestScheduler so that I am able to forward the time and check if the the correct methods are called on a mock of the View.
  • Therefore I am using RxJavaPlugins and RxJavaSchedulersHook to override the computation Scheduler to use my TestScheduler instead. I also keep a reference to the latter in my test class and use that in each unit test.

See this code, which almost works:

public class MainPresenterTest {

    private TestScheduler testScheduler;

    @Before
    public void setupSchedulers() {
        testScheduler = new TestScheduler();

        RxJavaPlugins.getInstance().registerSchedulersHook(new RxJavaSchedulersHook() {
            @Override
            public Scheduler getComputationScheduler() {
                return testScheduler;
            }

            @Override
            public Scheduler getIOScheduler() {
                return testScheduler;
            }

            @Override
            public Scheduler getNewThreadScheduler() {
                return testScheduler;
            }
        });

        RxAndroidPlugins.getInstance().registerSchedulersHook(new RxAndroidSchedulersHook() {

            @Override
            public Scheduler getMainThreadScheduler() {
                return Schedulers.immediate();
            }
        });
    }

    @Test
    public void nothingHappensInTheFirstThreeSeconds() {
        MainPresenter.View mockedMainPresenterView = mock(MainPresenter.View.class);
        MainPresenter mainPresenter = new MainPresenter(mockedMainPresenterView);

        mainPresenter.onResume();

        verifyZeroInteractions(mockedMainPresenterView);

        testScheduler.advanceTimeTo(2_999, TimeUnit.MILLISECONDS);

        verifyZeroInteractions(mockedMainPresenterView);
    }

    @Test
    public void theWholeSequenceCompletesAfterEightSeconds() {
        MainPresenter.View mockedMainPresenterView = mock(MainPresenter.View.class);
        MainPresenter mainPresenter = new MainPresenter(mockedMainPresenterView);

        mainPresenter.onResume();

        verifyZeroInteractions(mockedMainPresenterView);

        testScheduler.advanceTimeTo(8, TimeUnit.SECONDS);

        verify(mockedMainPresenterView).displayText("5");
        verify(mockedMainPresenterView).displayText("4");
        verify(mockedMainPresenterView).displayText("3");
        verify(mockedMainPresenterView).displayText("2");
        verify(mockedMainPresenterView).displayText("1");
        verify(mockedMainPresenterView).displayText("Hello World!");

        verifyNoMoreInteractions(mockedMainPresenterView);
    }

Actually, it works fine as long as there is only one test case. If I add another one, I get the following exception when @Before is running for the second time:

java.lang.IllegalStateException: Another strategy was already registered: ...

I have now done some research and found several candidates for a solution:

If you were relying on the ability to change the main thread scheduler over time (such as for tests), return a delegating scheduler from the hook which allows changing the delegate instance at will.

I am assuming this method would also work for any other RxJava scheduler - if I knew how to do it.

So, which of these (or any other) methods is preferred for resetting/changing the TestScheduler that's used for overriding Schedulers.computation() in between tests? In particular, how would the third alternative work exactly? How would that "delegating scheduler" look or work?

Thanks for any advice!

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions