Как протестировать конкретную реализацию - PullRequest
0 голосов
/ 08 октября 2019

Я много читаю о (модульном) тестировании и стараюсь максимально использовать мой повседневный рабочий процесс, но почему-то мне кажется, что я что-то не так делаю.

Давайтескажем, у меня есть функция, которая берет путь и на основе некоторых элементов этого пути создает имя для нового файла журнала. Путь может быть C:/my_project/dir_1/message01, и он должен преобразовать это в dir_1_log_01.txt. Имя функции: convertPathToLogfileName.

Если бы я хотел написать юнит-тест для этой функции, он мог бы выглядеть так:

def test_convertPathToLogfileName():   
    path = "C:/my_project/dir_1/message01"

    expected = "dir_1_log_01.txt"
    actual = convertPathToLogFileName(path)

    assertEqual(expected, actual)

Теперь я мог бы написать несколько тестов, таких какони проверяют все виды входных данных, если результат соответствует ожидаемому.

Но что, если в какой-то момент я решу, что соглашение об именах для выбранного мной файла журнала больше не то, что я хочу:Я бы изменил функцию, чтобы она выполняла мое новое требование, и все тесты не пройдут.

Это всего лишь простой пример, но я чувствую, что это часто так. В то время как я программирую, я придумываю новый способ сделать что-то, а затем мой тест не пройден.

Есть ли что-то, чего мне здесь не хватает, я проверяю не то? И если так, как бы вы подошли к этой ситуации? Или это просто так, и я должен принять это?

Ответы [ 2 ]

1 голос
/ 09 октября 2019

Есть ли что-то, чего мне здесь не хватает?

Пара вещей.

Во-первых, вам не обязательно все ваши тесты, чтобы указывать точныйповедение субъекта. Утверждение о том, что два представления точно равны друг другу, является хорошей отправной точкой в ​​смысле simplest thing that could possibly work, но это не единственный выбор, который у вас есть. Может быть столь же эффективно иметь набор тестов, каждый из которых устанавливает какое-то ограничение - тогда, когда вы вносите небольшое изменение в свое предполагаемое поведение, вам нужно всего лишь внести небольшое изменение среди тестов.

Еще один дизайн модулей;см. [Parnas 1971]. Основная идея здесь заключается в том, что каждый модуль моделируется на основе решения, и если мы меняем решение, мы заменяем этот модуль. Границы модулей действуют как переборки для изменений.

В вашем примере, вероятно, есть по крайней мере два модуля

path = "C:/my_project/dir_1/message01"
expected = "dir_1_log_01.txt"

Это выглядит так, будто вам понадобится функция parseдля извлечения интересной информации из пути и некоторой функции apply to template для выполнения чего-то интересного с извлеченной информацией.

Это может позволить вам написать утверждение типа

assertEquals(
    applyTemplate("dir_1", "01"),
    convertPathToLogFileName(path)
)

, а затем в другом местеу вас может быть что-то вроде

assertEquals(
    "dir_1_log_01.txt",
    applyTemplate("dir_1", "01")
)

Когда вы позже решите, что ваше написание должно измениться, вам нужно всего лишь изменить второе утверждение. См. Джеймс Шор, Тестирование без насмешек , для получения дополнительной информации об этой идее.

Что будет часто происходить в мире, управляемом тестами, так это то, что, обнаружив, что нам нужно изменить какое-то поведение, мы будемрефакторинг, чтобы создать модуль, основанный на решении, которое мы собираемся изменить, и ввести путь, по которому мы можем настроить, какой модуль участвует в нашей системе - все эти изменения могут быть сделаны без нарушения каких-либо существующих тестов. Затем мы начинаем вводить новые тесты, в которых описывается модуль замены и как он взаимодействует с остальной частью вашего решения.

Если вы посмотрите выше, вы увидите, что я вроде бы подразумевал это, введя applyTemplate гдеу вас было только convertPathToLogFileName - я реорганизовал convertPath для создания функции applyTemplate, и теперь я могу изменить поведение системы локально.

Это не спасет нас, когда у нас большое количествоиз переоборудованных тестов;мы не прикованы наручниками к конкретной реализации теста навсегда. Вместо этого мы смотрим на тесты, которые затрудняют изменение реализации, и рассматриваем, как мы могли бы изменить дизайн тестов, чтобы облегчить будущие изменения.

Тем не менее, ожидается определенная доработка -лучшее доказательство того, какой код нам нужно изменить в будущем, - какой код нам нужно изменить сейчас . Функции, которые не меняются, будут хорошо работать с переобученными тестами. Мы хотим вкладывать деньги на дизайн в те части кода, которые мы регулярно модифицируем.

1 голос
/ 08 октября 2019

Есть ли что-то, чего мне здесь не хватает, я проверяю не то? И если так, как бы вы подошли к этой ситуации? Или это просто так, и я должен принять это?

Не совсем. Это классическая ситуация «входящий X приводит к выходному Y».

Единственное, что вы можете , вероятно, изменить: возможно, после вы закончили с TDD, и выу вас есть разные маленькие тесты, которые проверяют различные аспекты контракта на вход / выход вашей тестируемой функции ... вы можете поместить это в таблицу.

Значение: скорее всего, здесь нет необходимости иметь 20 различных методов испытаний (которые все делают одно и то же). Почему бы не использовать простой список, который содержит пары «X in», должен привести к «Y out» точкам данных. Затем у вас есть один тест, который просматривает этот список и тестирует эти пары.

Но вернемся к вашему основному вопросу: вы не тестируете реализацию. Ваши тесты только выполняют тест на вход / выход черного ящика: X входит, ожидается выход Y.

Другими словами: ваши тесты подтверждают контракт , а не реализацию . Реализация - это код, который каким-то образом вычисляет правильный Y для определенного прихода X. Как именно это делается, не имеет значения ни для ваших «производственных пользователей», ни для ваших тестовых случаев!

Поэтому: если ваш контракт когда-либо изменится (полностью), то все ваши текущие тесты станут недействительными,И да, когда вы следуете TDD, это, вероятно, будет означать, что вы (более или менее) начинаете с нуля.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...