运行多个测试时,只有第一个测试通过TestScheduler(Kotlin)

时间:2017-07-21 15:51:31

标签: android unit-testing kotlin rx-java

我正在尝试使用Kotlin中的RxJava在MVP架构中测试我的演示者。我创建了一个测试规则,用TestScheduler替换普通的调度程序,以便能够测试异步请求:

class TestSchedulerRule : TestRule {

    val testScheduler = TestScheduler()

    override fun apply(base: Statement, d: Description): Statement {
        return object : Statement() {
            @Throws(Throwable::class)
            override fun evaluate() {
                RxJavaPlugins.setInitIoSchedulerHandler { testScheduler }
                RxJavaPlugins.setInitComputationSchedulerHandler { testScheduler }
                RxJavaPlugins.setInitNewThreadSchedulerHandler { testScheduler }
                RxJavaPlugins.setInitSingleSchedulerHandler { testScheduler }
                RxAndroidPlugins.setInitMainThreadSchedulerHandler { testScheduler }

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

这是我的测试课程。问题是每个测试都在通过,如果我单独运行它们,但如果我运行整个套件,只有第一个通过。 TestScheduler应该存在问题,因为如果我使用立即调度程序,它就可以工作。

class UserListPresenterTest {

    @Rule @JvmField
    val testSchedulerRule = TestSchedulerRule()

    @Mock
    lateinit var mockGetUsers: GetUsers

    @Mock
    lateinit var mockView: UserListView

    lateinit var userListPresenter: UserListPresenter

    @Before
    fun setup() {
        MockitoAnnotations.initMocks(this)
        userListPresenter = UserListPresenter(mockGetUsers)
    }

    @Test
    fun testGetUsers_errorCase_showError() {
        // Given
        val error = "Test error"
        val single: Single<List<UserViewModel>> = Single.create {
            emitter -> emitter?.onError(Exception(error))
        }

        // When
        `when`(mockGetUsers.execute(Mockito.anyInt(), Mockito.anyBoolean())).thenReturn(single)

        userListPresenter.attachView(mockView)
        userListPresenter.getUsers()

        testSchedulerRule.testScheduler.triggerActions()

        // Then
        verify(mockView).hideLoading()
        verify(mockView).showError()
    }

    @Test
    fun testGetUsers_successCaseFirstPage_clearList() {
        // Given
        val users = listOf(UserViewModel(1, "Name", 1000, ""))
        val single: Single<List<UserViewModel>> = Single.create {
            emitter -> emitter?.onSuccess(users)
        }

        // When
        `when`(mockGetUsers.execute(Mockito.anyInt(), Mockito.anyBoolean())).thenReturn(single)

        userListPresenter.attachView(mockView)
        userListPresenter.getUsers()

        testSchedulerRule.testScheduler.triggerActions()

        // Then
        verify(mockView).clearList()
    }
}

以下是我在控制台中遇到的错误:

Wanted but not invoked:
mockView.hideLoading();
-> at com.example.tamaskozmer.kotlinrxexample.UserListPresenterTest.testGetUsers_errorCase_showError(UserListPresenterTest.kt:57)
Actually, there were zero interactions with this mock.

    at com.example.tamaskozmer.kotlinrxexample.UserListPresenterTest.testGetUsers_errorCase_showError(UserListPresenterTest.kt:57)
    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 com.example.tamaskozmer.kotlinrxexample.TestSchedulerRule$apply$1.evaluate(TestSchedulerRule.kt:28)
    at org.junit.rules.RunRules.evaluate(RunRules.java:20)
    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:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:51)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
    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.AppMainV2.main(AppMainV2.java:131)

UserListPresenter类

class UserListPresenter(private val getUsers: GetUsers) : BasePresenter<UserListView>() {

    private val offset = 5

    private var page = 1
    private var loading = false

    fun getUsers(forced: Boolean = false) {
        loading = true
        if (forced) {
            resetPaging()
        }
        getUsers.execute(page, forced)
                .subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe({
                    users ->
                    loading = false
                    if (page == 1) {
                        view?.clearList()
                    }
                    view?.addUsersToList(users)
                    view?.hideLoading()
                    page++
                },
                {
                    loading = false
                    view?.showError()
                    view?.hideLoading()
                })
    }

    private fun resetPaging() {
        page = 1
    }

    fun onScrollChanged(lastVisibleItemPosition: Int, totalItemCount: Int) {
        if (!loading) {
            if (lastVisibleItemPosition >= totalItemCount - offset) {
                getUsers()
            }
        }

        if (loading && lastVisibleItemPosition >= totalItemCount) {
            view?.showLoading()
        }
    }
}

2 个答案:

答案 0 :(得分:0)

修复了同样的问题。您应该定义调度程序,而不是在规则中指定它们。在TestRule.apply将另一个实例用于新测试之后,RxAndroidPlugins将保留TestScheduler的第一个实例。

将设置调度程序处理程序放在随播对象中会有所帮助

companion object {

    val testScheduler: TestScheduler

    init {
        testScheduler = TestScheduler()
        RxAndroidPlugins.setInitMainThreadSchedulerHandler { _ -> testScheduler }
        RxJavaPlugins.setInitComputationSchedulerHandler { _ -> testScheduler }
        RxJavaPlugins.setInitIoSchedulerHandler { _ -> testScheduler }

    }
}

答案 1 :(得分:0)