Как протестировать приватные методы в Котлине? Я попытался добавить @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) из androidx.annotation.VisibleForTesting, но это не делает мою функцию частной
Вот как я это использую
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
fun doSomething() {}
[РЕДАКТИРОВАТЬ]
Я понимаю, что мне не стоит тестировать методы 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)
}
}
И я написал для него тесты
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
}
}
К сожалению, для тестов мне нужно вызвать частную функцию fun read(bufferedReader: BufferedReader): List<String>, потому что при издевательстве над Filefile.bufferedReader дает NullPointerExceptionНевозможно издеваться над классом BufferedWriter в junit
Вы не можете напрямую тестировать частные методы, и вы не можете сделать метод закрытым никаким другим способом, кроме ключевого слова private. Либо сделайте их internal, либо протестируйте только общедоступный API.
Тестирование частной функции - действительно плохая практика. Всегда проверяйте общедоступный API; не привязывайте свой тест к деталям реализации.
Для справки: разбиение на «,» - это примерно 5% того, что парсер CSV делает за вас. Серьезно: не делайте таких вещей. Используйте существующую надежную библиотеку парсера CSV. Вы изобретаете колесо и будете повторять все ошибки, с которыми люди сталкиваются при этом. Поверьте, я был там. Правильный синтаксический анализ CSV произвольный - жесткий.
Я знаю @GhostCat. Это задание на собеседование :)
Хорошо, тогда это имеет смысл ;-) ... и мой ответ наверняка не мешает другим людям приходить и отвечать по технической стороне дела!
вы пробовали mockk lib? он может тестировать частные методы
На это есть только ответ один: даже не пытайтесь.
Вы пишете исходный код для связи намерение. Если вы делаете что-то private, то это внутренняя деталь реализации. Он может измениться в следующую секунду.
что, если вы все еще хотите проверить правильность работы этой частной функции?
он может быть изменен, но я все равно хочу, чтобы он вел себя должным образом
Затем вы должны протестировать пути использования, которые приводят к этой частной детали.
Не отвечает на вопрос, OP не спрашивал, стоит ли тестировать частные методы, а скорее, как это сделать.
@Attila Всегда есть другие люди, которые дают "технический" ответ на такие вопросы. Но помните: контент здесь написан не только для ОП, задающего вопрос, но и для всех будущих читателей. И дело в том: это идея плохой для тестирования частных методов. Но, к сожалению, многие люди делают эту часть не понимать. Вы можете написать свой ответ и объяснить людям, как правильно выстрелить себе в ноги. Я предпочитаю им сказать: не стреляйте себе в ногу, потому что это плохо. Что в конечном итоге полезнее, читатели могут решить сами.
И чтобы быть действительно ясным: я не говорю, что вы не должны тестировать функциональность, которую реализуют частные методы. Но правильным местом было бы иметь достойные модульные тесты для общедоступного интерфейса и гарантировать, что эти тесты приводят к «полному» покрытию вашего производственного кода. Но делать частные методы «целью» отдельного теста - просто плохая идея. Это означает создание плохого дизайна и времяпрепровождение там, где его не следует тратить.
@GhostCat, я ценю рациональность вашего ответа, он действительно имеет смысл и приносит дополнительную пользу. Однако не следует ли добавлять ваш «нетехнический / прямой» ответ в качестве комментария только к вопросу OP, а не в качестве правильного ответа на вопрос?
@Attila По определению, комментарии на этом сайте непостоянны. Нет никакой гарантии, что кто-то их не отметит (больше не нужен или по какой-то другой причине) ... а затем какой-то модератор удалит комментарий. Поэтому, когда я хочу сказать что-то, что считаю «достойным, чтобы продлиться долго», я использую ответ вместо комментариев.
нравится:
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
}
вам нужно передать экземпляр объекта, для которого вы хотите вызвать метод, имя метода и необходимые аргументы
Вы можете использовать java-отражения:
Чтобы протестировать метод:
private fun printGreetings(name: String): String {
return "Hello, $name"
}
Достаточно:
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) )
}
Аннотация не может сделать функцию частной. Это может помочь Android Studio предупредить вас об использовании этой функции не только из тестового кода, но и из других источников.