RxJava Проблемы с тестированием Presenter в Android

Привет, я хочу протестировать ведущего Android с помощью mockito, и у меня много проблем. Вот код, который я хочу протестировать:

public class SignUpPresenter extends BasePresenter {
    private static final String TAG = SignUpPresenter.class.getSimpleName();

    private final AuthRepository mAuthRepository;
    private final SignUpView mView;

    public SignUpPresenter(@NonNull SignUpView view, @NonNull AuthRepository authRepository) {
        this.mAuthRepository = authRepository;
        this.mView = view;
    }

    public void signUp(@NonNull String username, @NonNull String password, @NonNull String email) {
        mCompositeDisposable.add(mAuthRepository.signUp(username, password, email)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribeWith(new DisposableSingleObserver<TokenWrapper>() {
                    @Override
                    public void onSuccess(TokenWrapper t) {
                        Log.i(TAG, "Signed Up: " + t.getUser().getUsername());
                        mView.onSuccessSignUp(t.getUser(), t.getToken());
                    }

                    @Override
                    public void onError(Throwable e) {
                        Log.e(TAG, "Sign Up: " + e.getMessage());
                        mView.onErrorSignUp(e.getMessage());
                    }
                }));
    }
}

Код теста:

public class SignUpPresenterTest {

    @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
    @Mock public SignUpView mView;
    @Mock public AuthRepository mRepository;

    SignUpPresenter mPresenter;

    User mUser = new User();

    @Before
    public void setUp() {
        mPresenter = new SignUpPresenter(mView, mRepository);
    }

    @Test
    public void onSuccessSignUpTest() throws InterruptedException {
        TokenWrapper token = new TokenWrapper(mUser, null);
        when(mRepository.signUp("Username", "password", "[email protected]"))
                .thenReturn(Single.just(token));

        mPresenter.signUp("Username", "password", "[email protected]");

        verify(mView).onSuccessSignUp(mUser, null);
    }

    @Test
    public void onSignUpErrorTest() {
        when(mRepository.signUp("Username", "password", "[email protected]"))
                .thenReturn(Single.error(new Throwable("Error")));

        mPresenter.signUp("Username", "password", "[email protected]");

        verify(mView).onErrorSignUp("Error");
    }

}

И исключение:

    Wanted but not invoked:
mView.onSuccessSignUp(
    com.andiag.trainingsquad.models.entities.User@d6e7bab,
    null
);
-> at com.andiag.trainingsquad.SignUpPresenterTest.onSuccessSignUpTest(SignUpPresenterTest.java:51)
Actually, there were zero interactions with this mock.

Wanted but not invoked:
mView.onSuccessSignUp(
    com.andiag.trainingsquad.models.entities.User@d6e7bab,
    null
);
-> at com.andiag.trainingsquad.SignUpPresenterTest.onSuccessSignUpTest(SignUpPresenterTest.java:51)
Actually, there were zero interactions with this mock.

    at com.andiag.trainingsquad.SignUpPresenterTest.onSuccessSignUpTest(SignUpPresenterTest.java:51)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.mockito.internal.junit.JUnitRule$1.evaluateSafely(JUnitRule.java:63)
    at org.mockito.internal.junit.JUnitRule$1.evaluate(JUnitRule.java:43)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:119)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:42)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:234)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:74)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)

Спасибо за помощь.


person iagocanalejas    schedule 21.02.2017    source источник
comment
stackoverflow.com/questions/ 57223218/   -  person Mohammad Elsayed    schedule 26.07.2019


Ответы (1)


Я думаю, что проблема заключается в задействованном фоновом потоке (.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())): модульные тесты завершаются до того, как выполняются тестируемые вычисления.

Вы должны попробовать сопоставить свои потоки с текущим в модульных тестах с помощью правила тестирования. Что-то вроде этого (мой фрагмент на Kotlin, но, надеюсь, общая идея ясна):

class SchedulersOverrideRule(private val scheduler: Scheduler = Schedulers.trampoline()) : TestRule {

    override fun apply(base: Statement, description: Description?): Statement {
        return object : Statement() {
            override fun evaluate() {
                RxJavaPlugins.setIoSchedulerHandler { scheduler }
                RxJavaPlugins.setComputationSchedulerHandler { scheduler }
                RxJavaPlugins.setNewThreadSchedulerHandler { scheduler }
                RxAndroidPlugins.setInitMainThreadSchedulerHandler { scheduler }
                RxAndroidPlugins.setMainThreadSchedulerHandler { scheduler }

                try {
                    base.evaluate()
                } finally {
                    RxJavaPlugins.reset()
                    RxAndroidPlugins.reset()
                }
            }
        }
    }
}

И применяйте это правило так же, как MockitoRule применяется в ваших тестах.

person AndroidEx    schedule 21.02.2017
comment
Все та же ошибка с использованием new TestScheduler() в качестве параметра конструктора. - person iagocanalejas; 21.02.2017
comment
@iagocanalejas извините, если я не ясно выразился. Попробуйте использовать Schedulers.trampoline(), который будет текущим потоком, в котором выполняется модульный тест. TestScheduler недостаточно. - person AndroidEx; 21.02.2017
comment
Я ищу эту проблему с 2 дней, но теперь для меня все более ясно. Я закончил тем, что использовал TestScheduler, и теперь я использую io.reactivex.schedulers.Schedulers.trampoline();, который возвращает объект io.reactivex.Scheduler. И очень хорошо работает с RxJava 2.0.8. Спасибо - person Yasin Kaçmaz; 21.04.2017