【问题标题】:Unit testing class with own coroutine scope具有自己的协程范围的单元测试类
【发布时间】:2019-10-07 02:22:04
【问题描述】:

我有一个看起来像这样的类

class Foo {
  private val scope = Job() + Dispatchers.IO
  val emissions = PublishSubject.create<Bar>()

  fun doSomething() {
    scope.launch {
      // Do a whole bunch of work...
      withContext(Dispatchers.Main) emissions.onNext(bar)
    }
  }
}

我正在尝试想出一种方法来对其进行单元测试。我已经尝试使范围可注入并编写类似

@Test fun testFoo() = runBlockingTest {
  Dispatchers.setMain(TestCoroutineDispatcher())
  val foo = Foo(this)
  foo.doSomething()
  foo.emissions.assertStuff()
}

但这似乎不起作用。断言发生在doSomething() 内部的协程完成之前。

我也尝试过使这个调度程序可注入,提供Dispatchers.Unconfined,但这也无济于事。这种方法有问题吗?

【问题讨论】:

  • 必须是PublishSubject吗?
  • 需要是SubjectRelay、`频道`或类似的东西

标签: kotlin kotlin-coroutines


【解决方案1】:

如果您能够将作业公开给公共 API,您可以尝试

class Foo {
  private val scope: CoroutineScope = CoroutineScope(Job() + Dispatchers.IO)
  val emissions = PublishSubject.create<Bar>()

  fun doSomething() = scope.launch {
    // Do a whole bunch of work...
    withContext(Dispatchers.Main) { emissions.onNext(bar) }
  }
}

class Test {
  private val testFoo = Foo()
  private val testObserver: TestObserver<Bar> = TestObserver.create()

  @BeforeEach
  fun setUp() {
    testFoo.emissions.subscribe(testObserver)
  }

  @Test fun testFoo() {
    runBlockingTest {
      Dispatchers.setMain(TestCoroutineDispatcher())
      testFoo.doSomething().join()
      testObserver.assertValue(bar)
    }
  }
}

【讨论】:

  • 暴露一个Job意味着外部调用者可以取消launch启动的协程,这是我不想要的。
【解决方案2】:

用这个库可以很好地测试异步代码:Awaitility (or its kotlin extension)

你会这样写:

@Test fun testFoo() {

  val foo = Foo()

  foo.doSomething()

  await().atMost(5, MILLIS).until(/* your check like - foo.emissions.onNext(bar) */);
}

【讨论】:

  • 嗯。这会起作用,但我想知道是否有更优雅的解决方案来阻止调度程序。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多