使用多个协程延迟测试函数
Testing Function with Multiple Coroutine Delays
有人可以建议如何对函数中协程延迟背后的代码行进行断言。我无法使用注入 dispatchers
和使用 runBlockingTest
来执行此操作。我还更新了我的项目依赖项并尝试使用较新的 runTest
无济于事。
请大家指教。
代码示例:
val liveData1 = MutableLiveData(false)
fun foo() {
doTheThing(liveData1, {lambda1(liveData1)})
}
fun doTheThing(liveData1: LiveData<Boolean>, f1: () -> Unit) {
if (!liveData1.value) {
f1()
}
}
fun lambda1(liveData1: LiveData<Boolean>) {
viewModelScope.launch(dispatchers.main) {
delay(1000)
liveData1.postValue(true)
delay(1000)
liveData1.postValue(false)
}
}
测试示例:
@ExperimentalCoroutinesApi
@Test `test doTheThing`() = runBlockingTest{
val subject = MyClass(TestCoroutineDispatchers())
val observer1 = subject.liveData1.test()
observers1.assertValueHistory(false)
subject.foo()
observers1.assertValueHistory(false, true, false) // fails here stating should have history [false]!=[false, true, false]
}
我已经检查过了,如果我将延迟设置为 0,那么我的断言是正确的。我已经通过调试器,测试总是将代码运行到第一个延迟,但从未达到延迟之后的代码。
LiveData 测试辅助函数:
fun <T> LiveData<T>.test(): TestObserver<T> = TestObserver.test(this)
********
public TestObserver<T> assertValueHistory(T... values) {
List<T> mValueHistory = valueHistory();
int size = mValueHistory.size();
if (size != values.length) {
throw fail("Value count differs; expected: " + values.length + " " + Arrays.toString(values)
+ " but was: " + size + " " + this.valueHistory);
}
for (int valueIndex = 0; valueIndex < size; valueIndex++) {
T historyItem = mValueHistory.get(valueIndex);
T expectedItem = values[valueIndex];
if (notEquals(expectedItem, historyItem)) {
throw fail("Values at position " + valueIndex + " differ; expected: " + valueAndClass(expectedItem) + " but was: " + valueAndClass(historyItem));
}
}
return this;
}
我最后做的是将 Dispatcher 包装在一个接口中。
interface IDispatcherProvider{
val main : Dispatcher
val io : Dispatcher
val default: Dispatcher
val unconfined: Dispatcher
}
这允许您的代码部署具有:
object DispatcherProvider : IDispatcherProvider{
val main : Dispatcher = Dispatchers.Main
val io : Dispatcher = Dispatchers.IO
val default: Dispatcher = Dispatchers.Default
val unconfined: Dispatcher = Dispatchers.Unconfined
}
然后你传递接口以便你可以注入 TestProvider:
object TestDispatcherProvider : IDispatcherProvider{
val main : Dispatcher = TestCoroutineDispatcher()
val io : Dispatcher = TestCoroutineDispatcher()
val default: Dispatcher = TestCoroutineDispatcher()
val unconfined: Dispatcher = TestCoroutineDispatcher()
}
这样您的测试将不会使用线程池,但会 运行 按顺序进行。如果您重新分配调度程序,还有另一种方法遵循相同的前提,但我不记得它是什么,这与我们所做的非常接近(使用 DI 传递)并且在我们的协程测试中一直运行良好.
所以在你的 class :
class SomeClass(private val dispatcher: IDispatcherProvider = DispatcherProvider){
val liveData1 = MutableLiveData(false)
fun foo() {
doTheThing(liveData1, {lambda1(liveData1)})
}
fun doTheThing(liveData1: LiveData<Boolean>, f1: () -> Unit) {
if (!liveData1.value) {
f1()
}
}
fun lambda1(liveData1: LiveData<Boolean>) {
viewModelScope.launch(dispatcher.main) {
delay(1000)
liveData1.postValue(true)
delay(1000)
liveData1.postValue(false)
}
}
}
我明白了。您需要使用以下调度程序。仅 TestCoroutineDispatcher() 是不够的...
@ExperimentalCoroutinesApi
@InternalCoroutinesApi
object SynchronousDispatchersWithNoDelay : Dispatchers {
override val io: CoroutineDispatcher
get() = NoDelayDispatcher()
override val main: CoroutineDispatcher
get() = NoDelayDispatcher()
class NoDelayDispatcher : CoroutineDispatcher(), Delay {
override fun scheduleResumeAfterDelay(timeMillis: Long, continuation: CancellableContinuation<Unit>) {
continuation.resume(Unit) {}
}
override fun dispatch(context: CoroutineContext, block: Runnable) {
block.run()
}
}
}
有人可以建议如何对函数中协程延迟背后的代码行进行断言。我无法使用注入 dispatchers
和使用 runBlockingTest
来执行此操作。我还更新了我的项目依赖项并尝试使用较新的 runTest
无济于事。
请大家指教。
代码示例:
val liveData1 = MutableLiveData(false)
fun foo() {
doTheThing(liveData1, {lambda1(liveData1)})
}
fun doTheThing(liveData1: LiveData<Boolean>, f1: () -> Unit) {
if (!liveData1.value) {
f1()
}
}
fun lambda1(liveData1: LiveData<Boolean>) {
viewModelScope.launch(dispatchers.main) {
delay(1000)
liveData1.postValue(true)
delay(1000)
liveData1.postValue(false)
}
}
测试示例:
@ExperimentalCoroutinesApi
@Test `test doTheThing`() = runBlockingTest{
val subject = MyClass(TestCoroutineDispatchers())
val observer1 = subject.liveData1.test()
observers1.assertValueHistory(false)
subject.foo()
observers1.assertValueHistory(false, true, false) // fails here stating should have history [false]!=[false, true, false]
}
我已经检查过了,如果我将延迟设置为 0,那么我的断言是正确的。我已经通过调试器,测试总是将代码运行到第一个延迟,但从未达到延迟之后的代码。
LiveData 测试辅助函数:
fun <T> LiveData<T>.test(): TestObserver<T> = TestObserver.test(this)
********
public TestObserver<T> assertValueHistory(T... values) {
List<T> mValueHistory = valueHistory();
int size = mValueHistory.size();
if (size != values.length) {
throw fail("Value count differs; expected: " + values.length + " " + Arrays.toString(values)
+ " but was: " + size + " " + this.valueHistory);
}
for (int valueIndex = 0; valueIndex < size; valueIndex++) {
T historyItem = mValueHistory.get(valueIndex);
T expectedItem = values[valueIndex];
if (notEquals(expectedItem, historyItem)) {
throw fail("Values at position " + valueIndex + " differ; expected: " + valueAndClass(expectedItem) + " but was: " + valueAndClass(historyItem));
}
}
return this;
}
我最后做的是将 Dispatcher 包装在一个接口中。
interface IDispatcherProvider{
val main : Dispatcher
val io : Dispatcher
val default: Dispatcher
val unconfined: Dispatcher
}
这允许您的代码部署具有:
object DispatcherProvider : IDispatcherProvider{
val main : Dispatcher = Dispatchers.Main
val io : Dispatcher = Dispatchers.IO
val default: Dispatcher = Dispatchers.Default
val unconfined: Dispatcher = Dispatchers.Unconfined
}
然后你传递接口以便你可以注入 TestProvider:
object TestDispatcherProvider : IDispatcherProvider{
val main : Dispatcher = TestCoroutineDispatcher()
val io : Dispatcher = TestCoroutineDispatcher()
val default: Dispatcher = TestCoroutineDispatcher()
val unconfined: Dispatcher = TestCoroutineDispatcher()
}
这样您的测试将不会使用线程池,但会 运行 按顺序进行。如果您重新分配调度程序,还有另一种方法遵循相同的前提,但我不记得它是什么,这与我们所做的非常接近(使用 DI 传递)并且在我们的协程测试中一直运行良好. 所以在你的 class :
class SomeClass(private val dispatcher: IDispatcherProvider = DispatcherProvider){
val liveData1 = MutableLiveData(false)
fun foo() {
doTheThing(liveData1, {lambda1(liveData1)})
}
fun doTheThing(liveData1: LiveData<Boolean>, f1: () -> Unit) {
if (!liveData1.value) {
f1()
}
}
fun lambda1(liveData1: LiveData<Boolean>) {
viewModelScope.launch(dispatcher.main) {
delay(1000)
liveData1.postValue(true)
delay(1000)
liveData1.postValue(false)
}
}
}
我明白了。您需要使用以下调度程序。仅 TestCoroutineDispatcher() 是不够的...
@ExperimentalCoroutinesApi
@InternalCoroutinesApi
object SynchronousDispatchersWithNoDelay : Dispatchers {
override val io: CoroutineDispatcher
get() = NoDelayDispatcher()
override val main: CoroutineDispatcher
get() = NoDelayDispatcher()
class NoDelayDispatcher : CoroutineDispatcher(), Delay {
override fun scheduleResumeAfterDelay(timeMillis: Long, continuation: CancellableContinuation<Unit>) {
continuation.resume(Unit) {}
}
override fun dispatch(context: CoroutineContext, block: Runnable) {
block.run()
}
}
}