【问题标题】:MockK - cannot mock same function twiceMockK - 不能两次模拟相同的函数
【发布时间】:2020-07-28 18:58:02
【问题描述】:

我正在尝试在两种情况下测试 getTopicNames 函数(如下):如果成功,如果不成功。

fun getTopicNames(): Either<Exception, Set<String>> =
    try {
        adminClient.listTopics()
            .names()
            .get()
            .right()
    } catch (exception: ExecutionException) {
        exception.left()
    }

这是我正在执行这两个场景的测试类。如果我单独运行每个测试,它们都会成功。如果我运行整个类,则第二个执行失败,因为由于某种原因,之前对 adminClient.listTopics() 的模拟被保留了。

这些是所有相关的版本:

  • 科特林:1.3.72

  • koin:2.1.6

  • junit:5.6.1

  • 模拟:1.10.0

class TopicOperationsTest {

    @BeforeEach
    fun start() {
        val testModule = module(createdAtStart = true) {
            single { mockk<AdminClient>() }
        }
        startKoin { modules(testModule) }
    }

    @AfterEach
    fun stop() {
        stopKoin()
    }

    @Test
    fun `getTopicNames() returns a Right with the topics names`() {
        val adminClient = get(AdminClient::class.java)

        val listOfTopicsToReturn = mockk<ListTopicsResult>()
        val expectedTopics = setOf("Topic1", "Topic2", "Topic3")

        every { adminClient.listTopics() } returns listOfTopicsToReturn
        every { listOfTopicsToReturn.names() } returns KafkaFuture.completedFuture(expectedTopics)

        println("listOfTopicsToReturn.names(): " + listOfTopicsToReturn.names())
        println("adminClient.listTopics(): " + adminClient.listTopics())
        println("getTopicNames(): " + getTopicNames())

        assertThat(getTopicNames().getOrElse { emptySet() }, `is`(expectedTopics))
    }

    @Test
    fun `getTopicNames() returns a Left if failing to get topic names`() {
        val adminClient = get(AdminClient::class.java)

        every { adminClient.listTopics() } throws ExecutionException("Some Failure", Exception())

        assertThat(getTopicNames(), IsInstanceOf(Either.Left::class.java))
    }
}

这是我得到的错误,原因是验证失败的测试是第一个运行的:

java.lang.AssertionError: 
Expected: is <[Topic1, Topic2, Topic3]>
     but: was <[]>
Expected :is <[Topic1, Topic2, Topic3]>
Actual   :<[]>
<Click to see difference>

已经在 BeforeEach 方法上尝试过 clearAllMocks(),但它并没有解决我的问题,因为我刚开始得到:

io.mockk.MockKException: no answer found for: AdminClient(#1).listTopics()

【问题讨论】:

  • 我不熟悉koin,但您确定get(AdminClient::class.java) 返回的是Mockked 实例吗?我的猜测是不是。
  • 是的,它返回模拟实例。

标签: kotlin testing junit5 koin mockk


【解决方案1】:

我找到了一个让一切正常的解决方案。它是以下各项的组合:

  • @TestInstance(TestInstance.Lifecycle.PER_CLASS)
  • 将模拟作为类对象
  • @BeforeEach 方法中的 MockKAnnotations.init(this)
  • clearMocks() 指定要清除的实际模拟(也应该适用于多个模拟,只需用逗号分隔。
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class TopicOperationsTest {

    private var adminClientMock = mockk<AdminClient>()

    @BeforeEach
    fun start() {
        MockKAnnotations.init(this)
        val testModule = module(createdAtStart = true) {
            single { adminClientMock }
        }
        startKoin { modules(testModule) }
    }

    @AfterEach
    fun stop() {
        clearMocks(adminClientMock)
        stopKoin()
    }

    @Test
    fun `getTopicNames() returns a Right with the topics names`() {
        val adminClient = get(AdminClient::class.java)

        val listOfTopicsToReturn = mockk<ListTopicsResult>()
        val expectedTopics = setOf("Topic1", "Topic2", "Topic3")

        every { adminClient.listTopics() } returns listOfTopicsToReturn
        every { listOfTopicsToReturn.names() } returns KafkaFuture.completedFuture(expectedTopics)

        assertThat(getTopicNames().getOrElse { emptySet() }, `is`(expectedTopics))
    }

    @Test
    fun `getTopicNames() returns a Left if failing to get topic names`() {
        val adminClient = get(AdminClient::class.java)

        every { adminClient.listTopics() } throws ExecutionException("Some Failure", Exception())

        assertThat(getTopicNames(), IsInstanceOf(Either.Left::class.java))
    }
}

【讨论】:

    猜你喜欢
    • 2020-07-05
    • 1970-01-01
    • 2019-05-22
    • 1970-01-01
    • 1970-01-01
    • 2016-04-18
    • 2016-01-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多