Expected-创建一个JUnit 5 Extension
类,以管理对TestCoroutineDispatcher
的使用。
Observed-无法访问在testDispatcher
类中创建的Extension
变量。
Test.kt
@ExtendWith(InstantExecutorExtension::class, MainCoroutineExtension::class)
class FeedLoadContentTests {
private val contentViewModel = ContentViewModel()
private fun FeedLoad() = feedLoadTestCases()
@ParameterizedTest
@MethodSource("FeedLoad")
@ExtendWith(MainCoroutineExtension::class)
fun `Feed Load`(test: FeedLoadContentTest) = testDispatcher.runBlockingTest {
// Some testing done here.
}
}
Extension.kt
class MainCoroutineExtension : BeforeEachCallback, AfterEachCallback {
val testDispatcher = TestCoroutineDispatcher()
override fun beforeEach(context: ExtensionContext?) {
Dispatchers.setMain(testDispatcher)
}
override fun afterEach(context: ExtensionContext?) {
Dispatchers.resetMain()
testDispatcher.cleanupTestCoroutines()
}
}
不要使用成员变量来存储和检索Jupiter扩展名中的值。而是使用扩展上下文的存储机制:https://junit.org/junit5/docs/5.5.1/api/org/junit/jupiter/api/extension/ExtensionContext.Store.html
为什么这么复杂?原因是在大多数情况下,要存储的值的生命周期与扩展对象本身不匹配。
这里有三种在理论上可行的实现。但是,最后一种解决方案是最好的,使用getStore
存储扩展值并使用ParameterResolver
注入参数,因为它可以确保生命周期安全。
谢谢@johanneslink,向正确的方向指导我!
TLDR-使用Programmatic Extension Registration。
此策略可与在TestCoroutineDispatcher
中创建的MainCoroutineExtension
正常工作,并且其生命周期通过测试生命周期实现进行管理。
Test.kt
class FeedLoadContentTests {
companion object {
@JvmField
@RegisterExtension
val mainCoroutineExtension = MainCoroutineExtension()
}
private val contentViewModel = ContentViewModel()
private fun FeedLoad() = feedLoadTestCases()
@ParameterizedTest
@MethodSource("FeedLoad")
@ExtendWith(MainCoroutineExtension::class)
fun `Feed Load`(test: FeedLoadContentTest) =
mainCoroutineExtension.testDispatcher.runBlockingTest {
// Some testing done here.
}
}
Extension.kt
class MainCoroutineExtension : BeforeEachCallback, AfterEachCallback {
val testDispatcher = TestCoroutineDispatcher()
override fun beforeEach(context: ExtensionContext?) {
Dispatchers.setMain(testDispatcher)
}
override fun afterEach(context: ExtensionContext?) {
Dispatchers.resetMain()
testDispatcher.cleanupTestCoroutines()
}
}
ParameterResolver
注入参数TLDR-使用ParameterResolver
。
此方法实现了ParameterResolver
,以便注入管理本地JUnit测试中的协程生命周期所需的ParameterResolver
。
Test.kt
TestCoroutineDispatcher
Extension.kt
@ExtendWith(LifecycleExtensions::class)
// The TestCoroutineDispatcher is injected here as a parameter.
class FeedLoadContentTests(val testDispatcher: TestCoroutineDispatcher) {
private val contentViewModel = ContentViewModel()
private fun FeedLoad() = feedLoadTestCases()
@ParameterizedTest
@MethodSource("FeedLoad")
fun `Feed Load`(test: FeedLoadContentTest) = testDispatcher.runBlockingTest {
// Some testing done here.
}
}
class LifecycleExtensions : = BeforeEachCallback, AfterEachCallback, ParameterResolver {
val testDispatcher = TestCoroutineDispatcher()
override fun beforeEach(context: ExtensionContext?) {
// Set Coroutine Dispatcher.
Dispatchers.setMain(testDispatcher)
...
}
override fun afterEach(context: ExtensionContext?) {
// Reset Coroutine Dispatcher.
Dispatchers.resetMain()
testDispatcher.cleanupTestCoroutines()
...
}
override fun supportsParameter(parameterContext: ParameterContext?,
extensionContext: ExtensionContext?) =
parameterContext?.parameter?.type == TestCoroutineDispatcher::class.java
override fun resolveParameter(parameterContext: ParameterContext?,
extensionContext: ExtensionContext?) =
testDispatcher
}
存储扩展名值并使用getStore
注入参数这里唯一的重构不同于上面的使用ParameterResolver
使用注入参数,是使用ParameterResolver
存储getStore
。
这不是将TestCoroutineDispatcher
存储为成员变量,这在并行运行测试时可能导致生命周期问题。
TestCoroutineDispatcher