Должен ли я модульный тест * что * или * как * для составных функций? - PullRequest
1 голос
/ 26 февраля 2020

У меня есть функции 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, который является лучшим в целом. Если ни один из подходов не является лучшим во всех случаях, я хотел бы понять, в каких случаях следует использовать каждый из них.

Ответы [ 2 ]

1 голос
/ 26 февраля 2020

Это зависит от того, как вы интерпретируете h:

  1. Это то, что представляет собой комбинацию f и g, независимо от того, как f и g может вести себя в будущем? Затем вы проверяете сантехнику.

  2. Это то, что производит значение, которое в данный момент вы вычисляете, используя f и g, но можете использовать альтернативная реализация? Затем проверьте выходное значение в соответствии с ожидаемым значением, заданным для ввода.

1 голос
/ 26 февраля 2020

Дело в том, что вы не хотите дублировать свои тестовые логи c.

Возможно, вы не захотите включать f и g логи c в свой тест для h.
В этом случае хорошо подходит насмешка, потому что она позволяет вам проверять только h. Если f изменяется и имеет регрессию, то тесты на h не пройдут, потому что его лог c все еще действителен.

Проблема в том, что вы добавляете больше слоев (единиц) тесты. Однако выгода заключается в том, что вы полностью изолируете свои тесты, и logi c тестируется только там, где он должен быть.

Что касается всего, есть баланс, который нужно найти. Но если ваша логика c сложная и включает несколько коллабораторов (например, f и g), то насмешка может снизить сложность теста.

Если вы знакомы с TDD, это напрямую Связанные с Mockist против Classicist. Если у вас есть время, я предлагаю вам посмотреть эти видео о Outside-In TDD. Вы многому научитесь.

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