How to test private methods in Kotlin? I tried to add @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
from androidx.annotation.VisibleForTesting
but it doesn’t make my function private
This is how I’m using it
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
fun doSomething() {}
[EDIT]
I understand that I shouldn't test private
methods, but it's now always trivial. What about below case.
I have a CsvReader class
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)
}
}
And I wrote tests for it
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
}
}
Unfortunately, for tests I need to call private function fun read(bufferedReader: BufferedReader): List<String>
because when mocking File
, file.bufferedReader
gives NullPointerException
Unable to mock BufferedWriter class in junit
You can't directly test private methods, and you can't make a method private any other way than the keyword private . Either make them internal or only test public API.
The short answer is that you shouldn't test private methods directly, but only their effects on the public methods that call them. Unit tests are clients of the object under test, much like the other classes in the code that are dependent on the object.
Use the TestVisible annotation to allow test methods to access private or protected members of another class outside the test class. These members include methods, member variables, and inner classes. This annotation enables a more permissive access level for running tests only.
You can use java reflections:
To test the method:
class ClassUnderTest {
private fun printGreetings(name: String): String {
return "Hello, $name"
}
}
That's enough:
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) )
}
like this:
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
}
you need to pass instance of the object you want the method to be called on, method name and required arguments
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With