Ada*_*itz 4 junit android kotlin android-testing junit5
预期- 创建一个 JUnit 5Extension类以管理TestCoroutineDispatcher.
观察到- 无法访问testDispatcher在Extension类中创建的变量。
测试.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.
}
}
Run Code Online (Sandbox Code Playgroud)
扩展.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()
}
}
Run Code Online (Sandbox Code Playgroud)
以下是理论上可行的三种实现方式。然而,最后一个解决方案是最好的,使用存储扩展值getStore并使用注入参数ParameterResolver,因为它可以确保生命周期安全。
感谢@johanneslink,指导我走向正确的方向!
TLDR - 使用编程扩展注册。
该策略按预期与TestCoroutineDispatcher中创建的项目一起工作MainCoroutineExtension,并且其生命周期通过测试生命周期实现进行管理。
测试.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.
}
}
Run Code Online (Sandbox Code Playgroud)
扩展.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()
}
}
Run Code Online (Sandbox Code Playgroud)
ParameterResolverTLDR - 使用ParameterResolver.
此方法实现了一个ParameterResolver以便TestCoroutineDispatcher在本地 JUnit 测试中注入管理 Coroutine 生命周期所需的内容。
测试.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.
}
}
Run Code Online (Sandbox Code Playgroud)
扩展.kt
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
}
Run Code Online (Sandbox Code Playgroud)
getStore使用注入参数ParameterResolver这里唯一的重构与上面使用的注入参数ParameterResolver不同,是使用getStore来存储TestCoroutineDispatcher. 使用它很重要context?.root,以避免为每个测试类创建注入值的多个实例。
这不是存储TestCoroutineDispatcher为成员变量,这可能会导致并行运行测试时出现生命周期问题。
扩展.kt
class LifecycleExtensions : BeforeAllCallback, AfterAllCallback, BeforeEachCallback,
AfterEachCallback, ParameterResolver {
...
override fun beforeEach(context: ExtensionContext?) {
// Set Coroutine Dispatcher.
Dispatchers.setMain(context?.root
?.getStore(STORE_NAMESPACE)
?.get(STORE_KEY, TestCoroutineDispatcher::class.java)!!)
...
}
override fun afterEach(context: ExtensionContext?) {
// Reset Coroutine Dispatcher.
Dispatchers.resetMain()
context?.root
?.getStore(STORE_NAMESPACE)
?.get(STORE_KEY, TestCoroutineDispatcher::class.java)!!.cleanupTestCoroutines()
...
}
override fun supportsParameter(parameterContext: ParameterContext?,
extensionContext: ExtensionContext?) =
parameterContext?.parameter?.type == TestCoroutineDispatcher::class.java
override fun resolveParameter(parameterContext: ParameterContext?,
extensionContext: ExtensionContext?) =
getTestCoroutineDispatcher(extensionContext).let { dipatcher ->
if (dipatcher == null) saveAndReturnTestCoroutineDispatcher(extensionContext)
else dipatcher
}
private fun getTestCoroutineDispatcher(context: ExtensionContext?) = context?.root
?.getStore(TEST_COROUTINE_DISPATCHER_NAMESPACE)
?.get(TEST_COROUTINE_DISPATCHER_KEY, TestCoroutineDispatcher::class.java)
private fun saveAndReturnTestCoroutineDispatcher(extensionContext: ExtensionContext?) =
TestCoroutineDispatcher().apply {
extensionContext?.root
?.getStore(TEST_COROUTINE_DISPATCHER_NAMESPACE)
?.put(TEST_COROUTINE_DISPATCHER_KEY, this)
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
7636 次 |
| 最近记录: |