简体   繁体   English

Android单元测试中如何调用回调?

[英]How do I invoke a callback in a unit test in Android?

I want to invoke a callback to assert the execution it makes.我想调用一个回调来断言它所做的执行。

I'm using MVVM in my app.我在我的应用程序中使用 MVVM。 In one of the view models I implemented, I want to make sure the ui state changes when a process is completed.在我实现的其中一个视图模型中,我想确保 ui state 在流程完成时发生变化。

In my HomeViewModel.kt I have:在我的HomeViewModel.kt中我有:

@HiltViewModel
class HomeViewModel
@Inject
constructor(
    private val storageRepository: StorageRepository,
    private val accountRepository: AccountRepository,
) : ViewModel() {
// First state of isLoading is true
var uiState = mutableStateOf(HomeUiState())

...

fun addListener() {
    viewModelScope.launch {
        storageRepository.addListener(
            accountRepository.getUserId(),
            ::onDocumentEvent,
            onComplete = { 
                uiState.value = uiState.value.copy(isLoading = false) 
            },
            onError = { 
                error -> onAddListenerFailure(error) 
            }
        )
    }
}

And I want to write the test:我想写测试:

Given homeViewModel.addListener()给定homeViewModel.addListener()

When storageRepository.addListener(...) completesstorageRepository.addListener(...) 完成时

Then uiState.isLoading is false那么uiState.isLoading 为 false

I've been searching for some time now and I have found some people referring to using captors from mockito but nothing that applies to my case.我已经搜索了一段时间,发现有些人提到使用 mockito 中的捕获器,但没有任何内容适用于我的情况。

This is what I have now这就是我现在所拥有的

@OptIn(ExperimentalCoroutinesApi::class)
internal class HomeViewModelTest {
  // mock repositories
  @Mock lateinit var storageRepository: StorageRepository
  @Mock lateinit var accountRepository: AccountRepository
  @Mock lateinit var logRepository: LogRepository

  // set dispatcher to be able to run tests
  private val dispatcher = StandardTestDispatcher()

  lateinit var callbackCaptor: KArgumentCaptor<() -> Unit>

  @Before
  fun setUp() {
    MockitoAnnotations.openMocks(this)
    Dispatchers.setMain(dispatcher)
  }

  @After
  fun tearDown() {
    Dispatchers.resetMain()
  }

  @Test
  fun `loading state is true when viewModel is created`() {
    val homeViewModel = HomeViewModel(storageRepository, accountRepository, logRepository)
    assertTrue(homeViewModel.uiState.value.isLoading)
  }

  @Test
  fun `loading state is false when listener is added successfully`() {
    val homeViewModel = HomeViewModel(storageRepository, accountRepository, logRepository)
    callbackCaptor = argumentCaptor()

    whenever(
        storageRepository.addListener(
          anyString(),
          anyOrNull(),
          callbackCaptor.capture(),
          anyOrNull()
        )
      )
      .thenAnswer { callbackCaptor.firstValue.invoke() }

    homeViewModel.addListener()

    // wait for mutable state to update
    dispatcher.scheduler.advanceUntilIdle()
    assertFalse(homeViewModel.uiState.value.isLoading)
  }
}

Of course, I'm open to hearing solutions using something else than captors.当然,我愿意听取使用俘虏以外的东西的解决方案。

I think you are not initialising the captor, try following我认为您没有初始化捕获器,请尝试以下操作

@Test
fun `loading state is false when listener completes its process`() {
    val homeViewModel = HomeViewModel(storageRepository, accountRepository, logRepository)

val callbackCaptor = argumentCaptor<() -> Unit>() //used kotlin mockito

    whenever(storageRepository.addListener(anyString(), any(), callbackCaptor.capture(), any()))
        .thenAnswer { callbackCaptor.firstValue.invoke() } 

    homeViewModel.addListener()

    // wait for mutable state to update
    dispatcher.scheduler.advanceUntilIdle()

    assertFalse(homeViewModel.uiState.value.isLoading)
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM