在Kotlin中测试私有方法

qba*_*ait 7 tdd android private kotlin

如何在Kotlin中测试私有方法?我尝试从中添加@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)androidx.annotation.VisibleForTesting但没有将我的函数设为私有

这就是我的使用方式

@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
fun doSomething() {}
Run Code Online (Sandbox Code Playgroud)

[编辑]

我知道我不应该测试private方法,但是现在它总是微不足道的。那下面的情况呢?

我有一个CsvReader类

class CsvReader(private val inputStream: InputStream, private val separator: String = "\t") {
    fun read(): List<String> {
        return read(inputStream.bufferedReader())
    }
    private fun read(bufferedReader: BufferedReader): List<String> {
        val line = bufferedReader.use { it.readLine() } // `use` is like try-with-resources in Java
        return parse(line)
    }
    private fun parse(line: String): List<String> {
        return line.split(separator)
    }
}
Run Code Online (Sandbox Code Playgroud)

我为此写了测试

class CsvReaderTest {
    private val stream = mock<InputStream>()
    private val reader = CsvReader(stream)
    private val bufferedReader = mock<BufferedReader>()
    @Test
    fun read() {
        whenever(bufferedReader.readLine()).thenReturn("Jakub\tSzwiec")
        reader.read(bufferedReader) shouldEqual listOf("Jakub", "Szwiec")
    }
    @Test
    fun readWhenEmpty() {
        whenever(bufferedReader.readLine()).thenReturn("")
        reader.read(bufferedReader) shouldEqual listOf("")
    }
    @Test
    fun throwIOExceptionWhenReadingProblems() {
        whenever(bufferedReader.readLine()).thenThrow(IOException::class.java)
        val read = { reader.read(bufferedReader) }
        read shouldThrow IOException::class
    }
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,测试中,我需要调用私有函数fun read(bufferedReader: BufferedReader): List<String>,因为嘲讽时Filefile.bufferedReader给出了NullPointerException 无法嘲笑在JUnit的BufferedWriter类

Pio*_*otr 14

您可以使用java反射:

测试方法:

class ClassUnderTest {
      private fun printGreetings(name: String): String {
        return "Hello, $name"
      }
}
Run Code Online (Sandbox Code Playgroud)

这就足够了:

  private val classUnderTest = spyk(ClassUnderTest()) 

  @Test
    fun `should return greetings`() {
      val method = classUnderTest.javaClass.getDeclaredMethod("printGreetings", String::class.java)
      method.isAccessible = true
      val parameters = arrayOfNulls<Any>(1)
      parameters[0] = "Piotr"

      assertEquals("Hello, Piotr", method.invoke(classUnderTest, *parameters) )
  }
Run Code Online (Sandbox Code Playgroud)

  • 仅供参考:无需“spyk”即可工作 (2认同)

Fil*_*lin 8

像这样:

fun callPrivate(objectInstance: Any, methodName: String, vararg args: Any?): Any? {
        val privateMethod: KFunction<*>? =
            objectInstance::class.functions.find { t -> return@find t.name == methodName }

        val argList = args.toMutableList()
        (argList as ArrayList).add(0, objectInstance)
        val argArr = argList.toArray()

        privateMethod?.apply {
            isAccessible = true
            return call(*argArr)
        }
            ?: throw NoSuchMethodException("Method $methodName does not exist in ${objectInstance::class.qualifiedName}")
        return null
    }
Run Code Online (Sandbox Code Playgroud)

您需要传递要调用该方法的对象的实例、方法名称和所需参数

  • 如果您确实需要测试“私有”函数,或者了解有关 Kotlin 反射的更多信息,这实际上非常酷。不过,一般来说,建议使用适当的可见性修饰符“internal”而不是“private”。 (2认同)

Gho*_*ica 7

对此只有一个答案:甚至不要尝试。

您编写源代码来传达意图。如果你做了一些事情,private那么这就是一个内部实现细节。它可能会在下一秒发生变化。

  • 如果您仍然想测试这个私有函数是否正常工作怎么办? (15认同)
  • @Attila 总是有其他人对此类问题给出“技术”答案。但请记住:这里的内容不仅是为提出问题的OP编写的,也是为所有未来的读者编写的。事实是:测试私有方法是一个**坏**的主意。但不幸的是:很多人**不明白**那部分。你可以写下你自己的答案,并向人们解释如何正确地向自己开枪。我更愿意告诉他们:避免搬起石头砸自己的脚,因为这是一件坏事。最终什么更有帮助,读者可以自己决定。 (7认同)
  • 没有回答这个问题,OP并没有问测试私有方法是否是一个好主意,而是问如何这样做。 (6认同)
  • 然后您应该测试通向该私有详细信息的使用路径。 (3认同)
  • @GhostCat,我很欣赏你的回答背后的理性,它确实有意义并带来了附加值。但是,您的“非技术/直接”答案不应该仅作为评论添加到OP问题而不是问题的正确答案吗? (2认同)