Модульное тестирование поведения Kotlin's ConflatedBroadcastChannel - PullRequest
0 голосов
/ 02 июля 2019

В новом проекте, над которым я сейчас работаю, у меня вообще нет зависимости от RxJava, потому что до сих пор мне это не нужно - сопрограммы решают проблему многопоточности довольно изящно.

В этот момент я наткнулся на требование иметь поведение, подобное BehaviorSubject, при котором можно подписаться на поток данных и получить последнее значение при подписке. Как я узнал, Channel обеспечивают очень похожее поведение в Kotlin, поэтому я решил попробовать их.

Из этой статьи, которую я узнал, ConflatedBroadcastChannel - это тип канала, который имитирует BehaviorSubject, поэтому я объявил следующее:

class ChannelSender {

    val channel = ConflatedBroadcastChannel<String>()

    fun sendToChannel(someString: String) {
         GlobalScope.launch(Dispatchers.Main) { channel.send(someString) }
    }
}

Для прослушивания канала я делаю это:


    class ChannelListener(val channelSender: ChannelSender) {
        fun listenToChannel() {
            channelSender.channel.consumeEach { someString ->
                if (someString == "A") foo.perform() 
                else bar.perform()
            }
        }
    }

Это работает, как и ожидалось, но на данный момент у меня возникают трудности с пониманием того, как выполнить модульное тестирование ChannelListener.

Я пытался найти что-то связанное здесь , но ни один из example-channel-**.kt классов не помог.

Любая помощь, предложение или исправление, связанные с моими неверными предположениями, приветствуется. Спасибо.

1 Ответ

0 голосов
/ 02 июля 2019

С помощью Алексея мне удалось получить следующий код, который отвечает на вопрос:

class ChannelListenerTest {

  private val val channelSender: ChannelSender = mock()

  private val sut = ChannelListener(channelSender)
  private val broadcastChannel = ConflatedBroadcastChannel<String>()

  private val timeLimit = 1_000L
  private val endMarker = "end"

  @Test
  fun `some description here`() = runBlocking {
    whenever(channelSender.channel).thenReturn(broadcastChannel)

    val sender = launch(Dispatchers.Default) {
      broadcastChannel.offer("A")
      yield()
    }

    val receiver = launch(Dispatchers.Default) {
      while (isActive) {
        val i = waitForEvent()
        if (i == endMarker) break
        yield()
      }
    }

    try {
      withTimeout(timeLimit) {
        sut.listenToChannel()
        sender.join()
        broadcastChannel.offer(endMarker) // last event to signal receivers termination
        receiver.join()
      }
      verify(foo).perform()
    } catch (e: CancellationException) {
      println("Test timed out $e")
    }
  }

  private suspend fun waitForEvent(): String =
      with(broadcastChannel.openSubscription()) {
        val value = receive()
        cancel()
        value
      }

}
...