У меня есть функции f
и g
, в которых уже есть набор модульных тестов, обеспечивающих правильность их поведения для некоторых известных пар ввода-вывода (плюс обработка исключений и т. Д. c).
Сейчас я создаю функцию h()
следующим образом:
def h(x):
return f(x) + g(2*x)
Какой хороший подход для модульного тестирования этой функции? Изменилось бы это, если бы h()
было существенно более сложным?
Мои мысли пока:
Я вижу две возможности для тестирования h()
.
1. Тестирование, если h()
выполняет правильное "слесарное дело"
Макет f()
, чтобы он возвращал y_0
при вызове с помощью ввода x_0
; макет g()
, чтобы он возвращал z_0
при вызове с помощью ввода 2*x_0
. Затем проверьте, что вызов h(x_0)
возвращает y_0+z_0
.
Преимущества :
- Очень просто реализовать тест.
- Может быстро найти ошибки, в которых я неправильно подключил выходы
f
и g
или где я вызвал их с неправильными аргументами (скажем, вызывая g(x)
вместо g(2*x)
в h()
).
Недостатки :
- Это тестирование как , а не , что
h()
должен делать. Если позже я захочу провести рефакторинг h()
, то, возможно, мне придется переписать эти типы тестов. - Если сантехника, указанная в тесте, не выдает намеченное поведение высокого уровня для
h()
, тогда эти тесты не поймают эту ошибку. Например, возможно, правильная сантехника должна была быть f(-x) + g(2*x)
, и я ошибся как в определении функции, так и в определении теста.
2. Тестирование что h()
должно сделать
Предположим, что цель h()
- вычислить сумму простых чисел ниже заданного аргумента. В этом случае естественный набор тестов для h()
будет включать тестирование с известными парами ввода-вывода. Что-то, что гарантирует, например, h(1)=2
, h(2)=5
, h(5)=28
, et c, не заботясь о , как h()
вычисляет эти числа.
Преимущества :
- Этот тип теста проверяет, действительно ли
h()
соответствует своему предполагаемому высокоуровневому поведению. Любые ошибки сантехники, которые изменят это, будут обнаружены. - Рефакторинг
h()
, вероятно, не потребует изменения набора тестов, и даже будет проще, поскольку тесты помогают нам гарантировать, что поведение функции не изменить.
Недостатки :
- В этом простом примере такие пары легко получить, поскольку отображение, которое выполняет
h()
, не очень сложно. (просто сложите n
первых простых чисел). Однако для очень сложного h()
единственным вариантом для создания таких пар может быть то, что я придумаю ввод x
и вычислю правильный вывод вручную. Это не представляется разумным, если h
очень сложно. - Поскольку для получения известных пар ввода-вывода требуется, чтобы я вычислил, что
f()
и g()
будут производить при определенном входном сигнале, вероятно, это будет некоторое дублирование усилий, так как я уже потратил некоторое время на создание модульных тестов для этих функций.
Смежный вопрос : Юнит тестирование составные функции .
Этот вопрос на первый взгляд очень похож на мой. Тем не менее, два наиболее проголосовавших ответа представляют совершенно разные подходы к проблеме (два подхода, которые я упомянул выше). Мой вопрос - попытка прояснить плюсы / минусы каждого подхода (и, возможно, изучить другие подходы) и потенциально установить sh, который является лучшим в целом. Если ни один из подходов не является лучшим во всех случаях, я хотел бы понять, в каких случаях следует использовать каждый из них.