Допустим, я начинаю разрабатывать ролевую игру с персонажами, которые атакуют других персонажей и тому подобное.
Применяя TDD, я делаю несколько тестов для проверки логики внутри Character.receiveAttack(Int)
метода. Что-то вроде этого:
@Test
fun healthIsReducedWhenCharacterIsAttacked() {
val c = Character(100) //arg is the health
c.receiveAttack(50) //arg is the suffered attack damage
assertThat(c.health, is(50));
}
Скажем, у меня есть 10 методов тестирования receiveAttack
метода. Теперь я добавляю метод Character.attack(Character)
(который вызывает receiveAttack
метод), и после нескольких циклов тестирования TDD я принимаю решение: Character.receiveAttack(Int)
должно быть private
.
Что происходит с предыдущими 10 тестами? Должен ли я удалить их? Должен ли я сохранить метод public
(я так не думаю)?
Этот вопрос не о том, как тестировать частные методы, а о том, как обращаться с ними после перепроектирования при применении TDD
internal
или эквивалент вашего языка для предотвращения его раскрытия. На самом деле ответ Кевина Клайна - это такой подход.Ответы:
В TDD тесты служат исполняемой документацией вашего проекта. Ваш дизайн изменился, поэтому, очевидно, ваша документация тоже должна!
Обратите внимание, что в TDD единственный способ, которым
attack
мог появиться метод, - это результат неудачного прохождения теста. Это значит,attack
что проверяется другим тестом. Это означает, что косвенноreceiveAttack
охватываетсяattack
тестами России. В идеале любое изменениеreceiveAttack
должно нарушить хотя бы один изattack
тестов.И если это не так, то есть функциональность,
receiveAttack
которая больше не нужна и больше не должна существовать!Таким образом, поскольку
receiveAttack
тестирование уже пройденоattack
, не имеет значения, выполняете ли вы свои тесты или нет. Если ваша среда тестирования позволяет легко тестировать приватные методы, и если вы решите тестировать приватные методы, вы можете оставить их. Но вы также можете удалить их, не теряя тестовое покрытие и уверенность.источник
Если метод достаточно сложен для тестирования, он должен быть общедоступным в некотором классе. Таким образом, вы рефакторинг от:
чтобы:
Переместите текущий тест для X.complexity в ComplexityTest. Затем текст X.something, насмешливо Сложность.
По моему опыту, рефакторинг в сторону небольших классов и более коротких методов приносит огромные выгоды. Их легче понять, проще тестировать, и в итоге их используют больше, чем можно было ожидать.
источник
this.health = this.health - attackDamage
). Возможно, извлечь его в другой класс - это слишком силовое решение, на данный момент.Здесь следует иметь в виду, что решение, которое вы принимаете, заключается в удалении метода из API . Любезность обратной совместимости предполагает
Тесты удаляются / заменяются, когда ваш API больше не поддерживает метод. На этом этапе приватный метод - это деталь реализации, которую вы должны иметь возможность рефакторинга.
В этот момент вы вернулись к стандартному вопросу о том, должен ли ваш набор тестов иметь прямой доступ к реализациям, а взаимодействовать исключительно через общедоступный API. Закрытый метод - это то, что мы можем заменить, не мешая при этом тестам . Поэтому я ожидаю, что пара тестов уйдет в прошлое - либо выйдет на пенсию, либо перейдет с реализацией к отдельному тестируемому компоненту.
источник