【发布时间】:2018-10-18 20:21:24
【问题描述】:
这是我的整个测试课:
@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 个单元测试,它们在单独运行时都通过了,但是当我运行整个测试类时,我的第 2 和第 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.
我已经尝试了我能想到的一切来解决这个问题:
- 我将我的待测类实例化移动到 @Before 函数中。
- 我尝试创建一个 @After 函数并在我的模拟上调用 Mockito.reset。
我应该提到我正在使用 nhaarman.mockitokotlin2 库,它是 argumentCaptor。
关于为什么这些测试单独运行时通过但作为一个类一起运行时失败的任何线索?
【问题讨论】:
-
我之前也遇到过这种情况。检查您是否有可以在测试中更改的全局变量。或许您可以在
@Before或@After中重置它们。