如何在JUnit 5扩展中存储值并在参数化测试中插入

问题描述 投票:0回答:2

概述

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()
    }
}
android kotlin junit android-testing junit5
2个回答
1
投票

不要使用成员变量来存储和检索Jupiter扩展名中的值。而是使用扩展上下文的存储机制:https://junit.org/junit5/docs/5.5.1/api/org/junit/jupiter/api/extension/ExtensionContext.Store.html

为什么这么复杂?原因是在大多数情况下,要存储的值的生命周期与扩展对象本身不匹配。


0
投票

这里有三种在理论上可行的实现。但是,最后一种解决方案是最好的,使用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
© www.soinside.com 2019 - 2024. All rights reserved.