gpt4 book ai didi

android - 如何运行协程作为单元测试的阻塞?

转载 作者:IT老高 更新时间:2023-10-28 13:47:25 31 4
gpt4 key购买 nike

我已经开始为我的 MVP Android 项目编写单元测试,但是我依赖于协程的测试间歇性地失败(通过日志记录和调试,我确认验证有时会提前发生,添加 delay 可以解决这个问题当然)

我已经尝试使用 runBlocking 进行包装,并且我从 org.jetbrains.kotlinx:kotlinx-coroutines-test 中发现了 Dispatchers.setMain(mainThreadSurrogate) ,但是尝试了这么多组合至今都没有取得任何成功。

abstract class CoroutinePresenter : Presenter, CoroutineScope {
private lateinit var job: Job

override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main

override fun onCreate() {
super.onCreate()
job = Job()
}

override fun onDestroy() {
super.onDestroy()
job.cancel()
}
}

class MainPresenter @Inject constructor(private val getInfoUsecase: GetInfoUsecase) : CoroutinePresenter() {
lateinit var view: View

fun inject(view: View) {
this.view = view
}

override fun onResume() {
super.onResume()

refreshInfo()
}

fun refreshInfo() = launch {
view.showLoading()
view.showInfo(getInfoUsecase.getInfo())
view.hideLoading()
}

interface View {
fun showLoading()
fun hideLoading()

fun showInfo(info: Info)
}
}

class MainPresenterTest {
private val mainThreadSurrogate = newSingleThreadContext("Mocked UI thread")

private lateinit var presenter: MainPresenter
private lateinit var view: MainPresenter.View

val expectedInfo = Info()

@Before
fun setUp() {
Dispatchers.setMain(mainThreadSurrogate)

view = mock()

val mockInfoUseCase = mock<GetInfoUsecase> {
on { runBlocking { getInfo() } } doReturn expectedInfo
}

presenter = MainPresenter(mockInfoUseCase)
presenter.inject(view)
presenter.onCreate()
}

@Test
fun onResume_RefreshView() {
presenter.onResume()

verify(view).showLoading()
verify(view).showInfo(expectedInfo)
verify(view).hideLoading()
}

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

我相信 runBlocking block 应该强制所有子 coroutineScopes 在同一个线程上运行,强制它们在继续验证之前完成。

最佳答案

CoroutinePresenter 类中,您正在使用 Dispatchers.Main。您应该能够在测试中更改它。尝试执行以下操作:

  1. uiContext: CoroutineContext 参数添加到演示者的构造函数中:

    abstract class CoroutinePresenter(private val uiContext: CoroutineContext = Dispatchers.Main) : CoroutineScope {
    private lateinit var job: Job

    override val coroutineContext: CoroutineContext
    get() = uiContext + job

    //...
    }

    class MainPresenter(private val getInfoUsecase: GetInfoUsecase,
    private val uiContext: CoroutineContext = Dispatchers.Main
    ) : CoroutinePresenter(uiContext) { ... }
  2. 更改 MainPresenterTest 类以注入(inject)另一个 CoroutineContext:

    class MainPresenterTest {
    private lateinit var presenter: MainPresenter

    @Mock
    private lateinit var view: MainPresenter.View

    @Mock
    private lateinit var mockInfoUseCase: GetInfoUsecase

    val expectedInfo = Info()

    @Before
    fun setUp() {
    // Mockito has a very convenient way to inject mocks by using the @Mock annotation. To
    // inject the mocks in the test the initMocks method needs to be called.
    MockitoAnnotations.initMocks(this)

    presenter = MainPresenter(mockInfoUseCase, Dispatchers.Unconfined) // here another CoroutineContext is injected
    presenter.inject(view)
    presenter.onCreate()
    }

    @Test
    fun onResume_RefreshView() = runBlocking {
    Mockito.`when`(mockInfoUseCase.getInfo()).thenReturn(expectedInfo)

    presenter.onResume()

    verify(view).showLoading()
    verify(view).showInfo(expectedInfo)
    verify(view).hideLoading()
    }
    }

关于android - 如何运行协程作为单元测试的阻塞?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53900118/

31 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com