为什么我的Android单元测试一起运行时会失败,但是单独运行时会通过?

时间:2018-10-18 20:21:25

标签: android mockito

这是我的整个考试班:

@RunWith(JUnit4::class)
class ExplorerRemoteImplTest {

    // Mocks
    private val mockDatabase = mock<FirebaseFirestore>(
            defaultAnswer = RETURNS_DEEP_STUBS
    )
    private val mockQuerySnapshot = mock<QuerySnapshot>()
    private val mockQuerySnapshotTask = mock<Task<QuerySnapshot>>()

    // Class under test
    private lateinit var explorerRemoteImpl: ExplorerRemoteImpl

    // Others
    private val poiList = listOf(TestDataFactory.makePoiRepo(),TestDataFactory.makePoiRepo())

    @Before
    fun setup(){
        //create instance of class under test
        explorerRemoteImpl = ExplorerRemoteImpl(mockDatabase)


        // Step #1 return the query Task on get().
        whenever(mockDatabase.collection(ArgumentMatchers.anyString()).orderBy(ArgumentMatchers.anyString()).get()).thenReturn(mockQuerySnapshotTask)

        // Step #2 return a queryTask when registering the listener
        whenever(mockQuerySnapshotTask.addOnCompleteListener(anyOrNull()))
                .thenReturn(mockQuerySnapshotTask)

        // Step #3 task IS successful is stubbed

        // Step #4 the results of the task is a QuerySnapshot
        whenever(mockQuerySnapshotTask.result).thenReturn(mockQuerySnapshot)

        // Step #5 QuerySnapshot = is empty or not is stubbed

        // Step #6 when we try to convert snapShot to objects
        whenever(mockQuerySnapshot.toObjects(PoiRepository::class.java)).thenReturn(poiList)
    }

    private fun stubQuerySnapshotIsEmpty(boolean: Boolean){
        whenever(mockQuerySnapshot.isEmpty).thenReturn(boolean)
    }

    private fun stubQueryTaskIsSuccessful(boolean: Boolean){
        whenever(mockQuerySnapshotTask.isSuccessful).thenReturn(boolean)
    }

    @After
    fun onEnd(){
        Mockito.reset(mockQuerySnapshotTask)
        Mockito.reset(mockDatabase)
        Mockito.reset(mockQuerySnapshot)
    }

    @Test
    fun getPoisCompletes() {

        // GIVEN
        stubQueryTaskIsSuccessful(true)
        stubQuerySnapshotIsEmpty(false)

        val testObserver = explorerRemoteImpl.getPois().test()

        // Trigger callback reply
        // see: https://fernandocejas.com/2014/04/08/unit-testing-asynchronous-methods-with-mockito/
        val captor = argumentCaptor<OnCompleteListener<QuerySnapshot>>()
        verify(mockQuerySnapshotTask).addOnCompleteListener(captor.capture())
        captor.lastValue.onComplete(mockQuerySnapshotTask)

        verify(mockQuerySnapshotTask, times(1)).addOnCompleteListener(anyOrNull())

        // THEN
        testObserver
                .assertNoErrors()
                .assertValueCount(1)
                .assertComplete()
    }

    @Test
    fun getPoisCompletesOnEmptyQuerySnapshot() {

        // GIVEN
        stubQueryTaskIsSuccessful(true)
        stubQuerySnapshotIsEmpty(true)

        val testObserver = explorerRemoteImpl.getPois().test()

        // Trigger callback reply
        // see: https://fernandocejas.com/2014/04/08/unit-testing-asynchronous-methods-with-mockito/
        val captor = argumentCaptor<OnCompleteListener<QuerySnapshot>>()
        verify(mockQuerySnapshotTask).addOnCompleteListener(captor.capture())
        captor.firstValue.onComplete(mockQuerySnapshotTask)

        // THEN
        testObserver
                .assertNoErrors()
                .assertValueCount(0)
                .assertComplete()

        Mockito.verify(mockQuerySnapshotTask, times(1)).addOnCompleteListener(anyOrNull())
    }

    @Test
    fun getPoisErrorsOnNoSuccessQuerySnapshot() {

        // GIVEN
        stubQueryTaskIsSuccessful(false)
        stubQuerySnapshotIsEmpty(true)

        val testObserver = explorerRemoteImpl.getPois().test()

        // Trigger callback reply
        val captor = argumentCaptor<OnCompleteListener<QuerySnapshot>>()
        verify(mockQuerySnapshotTask).addOnCompleteListener(captor.capture())
        captor.firstValue.onComplete(mockQuerySnapshotTask)

        Mockito.verify(mockQuerySnapshotTask, times(1)).addOnCompleteListener(anyOrNull())

        // task Exception not mocked, so unknown is passed via Elvis operator
        testObserver.assertError(UnknownError::class.java)
    }
}

有3个单元测试,并且分别运行时都通过,但是当我运行整个测试类时,我的第二个和第三个测试失败,并显示以下错误:

Wanted but not invoked:
task.addOnCompleteListener(
    <Capturing argument>
);
-> at com.loc8r.seattleexplorer.remote.ExplorerRemoteImplTest.getPoisCompletesOnEmptyQuerySnapshot(ExplorerRemoteImplTest.kt:113)
Actually, there were zero interactions with this mock.

我已尽力解决该问题:

  1. 我将受测试实例化的类移到了@Before函数中。
  2. 我尝试创建一个@After函数,并在模拟中调用Mockito.reset。

我应该提到我正在使用nhaarman.mockitokotlin2库,它是argumentsCaptor。

有任何线索说明为什么这些测试在单独运行时通过但在作为一个类一起运行时却失败了?

1 个答案:

答案 0 :(得分:0)

您是否尝试过避免使用被测试类的单个共享实例(在您的情况下为ExplorerRemoteImpl?尝试为每个测试方法创建一个新实例。

实际上,我正面临与您上述完全相同的问题,但是由于我尝试测试单例,因此我无法重新实例化我的被测类。

更新: 我已经重构了单例的实现,因此对于测试,我可以为每个测试方法实例化“被测类”。该问题不再发生。