Как протестировать общий поток, управляемый viewModelScope?

#android #kotlin #android-viewmodel #kotlin-coroutines

Вопрос:

У меня есть SharedFlow . Когда ViewModel он будет создан, я изменю значение на Val1 . После этого я использую viewModelScope , чтобы сделать некоторую поддельную задержку 3 seconds , а затем изменить значение на Val2 .

 class MyViewModel : ViewModel() {

    val x = MutableSharedFlow<String>()

    init {
        x.tryEmit("Val1")
        viewModelScope.launch {
            delay(3000)
            x.tryEmit("Val2")
        }
    }
}
 

Вопрос

  1. Как мне проверить начальное значение Val1 ?
  2. Как проверить, изменилось ли значение Val2 после задержки?

Комментарии:

1. Если у вас нет буфера воспроизведения, никто никогда не сможет получить это начальное значение, потому что оно будет выдано и потеряно до того, как у подписчиков появится возможность начать сбор общего потока

Ответ №1:

Чтобы протестировать его, вам нужен способ ввести свой контекст тестирования. Обычно это делается путем установки его в качестве Dispatchers.Main.

Тогда самый простой путь — использовать MutableStateFlow вместо MutableSharedFlow . Вот пример:

 class MyViewModel : ViewModel() {

    val x = MutableStateFlow("Val1")

    init {
        viewModelScope.launch {
            delay(3000)
            x.tryEmit("Val2")
        }
    }
}

class MyViewModelTests {
    private val testDispatcher = TestCoroutineDispatcher()

    @Before
    fun setUp() {
        Dispatchers.setMain(testDispatcher)
    }

    @Test
    fun test() = runBlocking {
        // given
        val myViewModel = MyViewModel()

        // then
        assertEquals("Val1", myViewModel.x.value)

        // when
        testDispatcher.advanceTimeBy(3000)

        // then
        assertEquals("Val2", myViewModel.x.value)
    }
}
 

Если вы хотите протестировать MutableSharedFlow , вам лучше перенести свою логику из конструктора в какую-нибудь функцию, например onCreate . Затем вы должны собрать и понаблюдать, как меняются ваши ценности. Вот пример (мы могли бы сделать лучший с помощью некоторой библиотеки тестирования, такой как Turbine):

 class MyViewModel : ViewModel() {

    val x = MutableSharedFlow<String>()

     fun onCreate() {
        viewModelScope.launch {
            x.emit("Val1")
            delay(3000)
            x.emit("Val2")
        }
    }
}

class MyViewModelTests {
    private val testDispatcher = TestCoroutineDispatcher()

    @Before
    fun setUp() {
        Dispatchers.setMain(testDispatcher)
    }

    @Test
    fun test() = runBlocking {
        // given
        val myViewModel = MyViewModel()
        var xChangeHistory = mapOf<Long, String>()
        myViewModel.viewModelScope.launch {
            myViewModel.x.collect {
                xChangeHistory  = testDispatcher.currentTime to it
            }
        }

        // then
        myViewModel.onCreate()
        testDispatcher.advanceUntilIdle()

        // then
        assertEquals(mapOf(0L to "Val1", 3000L to "Val2"), xChangeHistory)
    }
}
 

Комментарии:

1. Я знаю, как проверить поток состояний. Вопрос в том, как мне это сделать SharedFlow ?

2. Разве вторая часть не дает вам ответа? Если вы хотите сохранить эту функцию в качестве конструктора, установите replay для параметра SharedFlow значение 1, и тогда будет возможно тестирование.

3. Я прошу прощения. Я не смогу перенести свою логику из init блока в метод.