Как вы говорите, что ваши юнит-тесты верны? - PullRequest
20 голосов
/ 18 февраля 2009

Я проводил незначительные юнит-тесты в разные моменты своей карьеры. Всякий раз, когда я начинаю погружаться в это снова, меня всегда беспокоит, как доказать, что мои тесты верны. Как я могу сказать, что в моем модульном тесте нет ошибки? Обычно я запускаю приложение, доказываю, что оно работает, а затем использую модульный тест как своего рода регрессионный тест. Каков рекомендуемый подход и / или как вы подходите к этой проблеме?

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

Edit2: Для аргументов «модульное тестирование предназначено для того, чтобы убедиться, что ваши изменения ничего не нарушают» и «это произойдет только в том случае, если тест имеет тот же недостаток, что и код», что, если тест переходит? С плохим тестом можно пройти как хороший, так и плохой код. Мой главный вопрос в том, что хорошего в модульном тестировании, поскольку, если ваши тесты могут быть ошибочными, вы не сможете реально повысить свою уверенность в своем коде, не сможете доказать, что ваш рефакторинг сработал, и не можете доказать, что вы соответствуете спецификации? 1005 *

Ответы [ 16 ]

15 голосов
/ 18 февраля 2009

Юнит-тест должен выражать «контракт» того, что вы тестируете. Это более или менее спецификация модуля, помещенного в код. Таким образом, учитывая спецификации, должно быть более или менее очевидно, являются ли юнит-тесты «правильными».

Но я бы не слишком беспокоился о «правильности» модульных тестов. Они являются частью программного обеспечения, и, как таковые, они также могут быть неверными. Суть модульных тестов - от моего POV - в том, что они гарантируют, что «контракт» вашего программного обеспечения не будет нарушен случайно . Вот что делает модульные тесты такими ценными: вы можете копаться в программном обеспечении, реорганизовывать некоторые части, изменять алгоритмы в других, и ваши модульные тесты сообщат вам, если вы что-то сломали. Даже неправильные модульные тесты скажут вам об этом.

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

7 голосов
/ 18 февраля 2009

Существует два способа обеспечить правильность ваших юнит-тестов:

  • TDD: сначала напишите тест, затем напишите код, который он должен тестировать. Это означает, что вы получаете см. , они терпят неудачу. Если вы знаете, что он обнаруживает как минимум некоторые классы ошибок (например, «Я не реализовал какую-либо функциональность в функции, которую хочу протестировать»), то вы знаете, что она не совсем бесполезна. Это может все же позволить некоторым другим ошибкам ускользнуть, но мы знаем, что тест не является полностью неправильным.
  • Есть много тестов. Если один тест пропустит некоторые ошибки, они, скорее всего, приведут к дальнейшим ошибкам, что приведет к сбою других тестов. Когда вы заметите это и исправите код, вызывающий сбой, у вас появится возможность проверить, почему первый тест не уловил ошибку, как ожидалось.

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

7 голосов
/ 18 февраля 2009

Ну, Дейкстра классно сказал:

"Тестирование показывает присутствие, а не отсутствие ошибок "

Итак, как бы вы написали модульный тест для функции add (int, int)?

IOW, это сложный вопрос.

3 голосов
/ 20 мая 2009

У меня был тот же вопрос, и, прочитав комментарии, я теперь думаю (с должным уважением к предыдущим ответам):

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

Как и во всех нетривиальных начинаниях, вы никогда не можете быть на 100% уверены. Правильная цель модульных тестов - уменьшить количество ошибок , а не устранить их. В частности, как уже отмечали другие, когда вы вносите изменения позже, это может случайно что-то сломать. Модульные тесты - это всего лишь один инструмент для уменьшения ошибок, и, конечно, не должен быть единственным. В идеале вы должны сочетать модульное тестирование с проверкой кода и надежным контролем качества, чтобы снизить количество ошибок до допустимого уровня.

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

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

Если вы ДЕЙСТВИТЕЛЬНО хотели применить второй уровень модульного тестирования, чтобы доказать правильность своих модульных тестов, вы можете сделать это, но это может привести к уменьшению отдачи. Чтобы посмотреть на это искусственно:

Предположим, что модульное тестирование уменьшает количество производственных ошибок на 50%. Затем вы пишете мета-модульные тесты (модульные тесты для поиска ошибок в модульных тестах). Скажите, что это находит проблемы с вашими юнит-тестами, снижая уровень ошибок в производстве до 40%. Но для написания мета-модульных тестов потребовалось 80% времени, как и для написания юнит-тестов. За 80% усилий вы получили только еще 20% прироста. Может быть, написание мета-мета-модульных тестов дает вам еще 5 процентных пунктов, но теперь опять же, что заняло 80% времени, которое потребовалось для написания мета-юнит-тестов, так что за 64% усилий написания юнит-тестов вы 50%) вы получили еще 5%. Даже при значительно более либеральных цифрах это не эффективный способ тратить ваше время.

В этом сценарии ясно, что проходить точку написания модульных тестов не стоит усилий.

3 голосов
/ 18 февраля 2009

Для того, чтобы это было проблемой, ваш код должен был содержать ошибки, которые по совпадению приводят к прохождению ваших тестов. Это случилось со мной недавно, когда я проверял, что данное условие (а) вызывает сбой метода. Тест пройден (то есть метод не пройден), но прошел, потому что другое условие (b) вызвало сбой. Тщательно напишите свои тесты и убедитесь, что модульные тесты тестируют одну вещь.

Как правило, тесты не могут быть написаны, чтобы доказать, что код не содержит ошибок. Это шаг в правильном направлении.

2 голосов
/ 19 февраля 2009

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

Как узнать, хороши ли мои модульные тесты?

Вы не можете проверить период логической части! Если ваш код говорит, что 2 + 2 = 5, а ваш тест проверяет, что 2 + 2 = 5, то для вас 2 + 2 - 5. Чтобы написать хорошие модульные тесты, вы ДОЛЖНЫ хорошо понимать область, с которой работаете. Когда вы знаете, что вы пытаетесь выполнить, вы напишите хорошие тесты и хороший код для этого. Если у вас много юнит-тестов и ваши предположения неверны, то рано или поздно вы обнаружите свои ошибки.

2 голосов
/ 18 февраля 2009
  1. сложность кода модульного теста (или должна быть) меньше (часто на несколько порядков меньше), чем у реального кода
  2. Вероятность того, что вы закодируете ошибку в своем модульном тесте, которая точно соответствует ошибке в вашем реальном коде, намного меньше, чем просто кодирует ошибку в вашем реальном коде (если вы кодируете ошибку в своем модульном тесте, которая не соответствует ошибка в вашем реальном коде, она должна потерпеть неудачу). Конечно, если вы сделали неправильные предположения в своем реальном коде, вы, вероятно, снова сделаете то же самое предположение - хотя умственные способности модульного тестирования должны все же уменьшить даже этот случай
  3. Как уже упоминалось, когда вы пишете модульный тест, у вас (или должен быть) другой настрой. При написании реального кода вы думаете «как мне решить эту проблему». При написании модульного теста вы думаете: «Как мне проверить все возможные пути, которые могут это сломать»

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

Конечно, должен прийти момент, когда вы будете достаточно уверены в своих юнит-тестах, например, при проведении рефакторинга. Достижение этого пункта обычно является лишь примером опыта и интуиции (хотя есть инструменты покрытия кода, которые помогают).

2 голосов
/ 18 февраля 2009

Я полагаю, что написание теста вначале (до написания кода) является довольно хорошим способом убедиться, что ваш тест действителен.

Или вы можете написать тесты для своих модульных тестов ...: P

1 голос
/ 01 июня 2013

Модульные тесты - это ваши требования конкретизированные. Я не знаю как вы, но мне нравится указывать требования перед началом работы с кодом (TDD). Написав их и рассматривая их как любой другой фрагмент вашего кода, вы начнете чувствовать себя уверенно, вводя новые функции, не нарушая старые функции. Чтобы убедиться, что весь ваш код необходим и что тесты действительно тестируют код, который я использую pitest (другие варианты для тестирования мутаций существуют для других языков). Для меня непроверенный код - это глючный код, каким бы чистым он ни был.

Если тест тестирует сложный код и сам является сложным, я часто пишу тесты для своих тестов ( пример ).

1 голос
/ 05 мая 2010

Доминик упомянул, что «для того, чтобы это было проблемой, ваш код должен был содержать ошибки, которые по совпадению приводят к прохождению ваших тестов». Один способ, который вы можете использовать, чтобы увидеть, если это проблема, это тестирование мутаций . Он вносит изменения в ваш код и проверяет, не вызывает ли он модульных тестов. Если это не так, то это может указывать на области, где тестирование не на 100% тщательное.

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