Как вы говорите, что ваши модульные тесты верны?

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

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

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

Ответ 1

unit test должен выразить "контракт" того, что вы тестируете. Это более или менее спецификация устройства, помещенного в код. Таким образом, учитывая спецификации, должно быть более или менее очевидно, являются ли модульные тесты "правильными".

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

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

Ответ 2

Хорошо, Дейкстра лихо сказал:

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

IOW, как бы вы написали unit test для функции add (int, int)?

IOW, это тяжело.

Ответ 3

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

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

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

Ответ 4

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

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

Ответ 5

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

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

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

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

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

Если вы хотите применить второй уровень модульного тестирования, чтобы подтвердить правильность ваших модульных тестов, вы можете сделать это, но оно будет уменьшаться. Чтобы посмотреть на это поддельно:

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

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

Ответ 6

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

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

Ответ 7

  • Сложность кода unit test (или должна быть) меньше (на несколько порядков меньше), чем реальный код
  • Вероятность вашего кодирования ошибки в вашем unit test, которая точно соответствует ошибке в вашем реальном коде, намного меньше, чем просто кодирование ошибки в вашем реальном коде (если вы кодируете ошибку в своем unit test, который не работает) t соответствует ошибке в вашем реальном коде, это должно потерпеть неудачу). Конечно, если вы сделали неправильные предположения в своем реальном коде, вы, вероятно, повторите одно и то же предположение - хотя набор умственных единиц для модульного тестирования еще должен уменьшить даже этот случай
  • Как уже упоминалось, когда вы пишете unit test, у вас есть (или должен иметь) другой набор разума. При написании реального кода вы думаете "как решить эту проблему". При написании unit test, о котором вы думаете, "как я могу проверить каждый способ, которым это может сломаться"

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

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

Ответ 8

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

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

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

Ответ 9

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

Ответ 10

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

  • Были ли у вас какие-либо дефекты, о которых сообщалось в ходе ручного тестирования, а unit test не удалось поймать (хотя это и было причиной), потому что в вашем тесте была ошибка?
  • Были ли у вас ложные негативы в прошлом?
  • Являются ли ваши тесты устройств достаточно простыми?
  • Вы пишете их перед новым кодом или, по крайней мере, параллельно?

Ответ 11

Вы не можете доказать, что тесты правильные, и если вы пытаетесь, вы делаете это неправильно.

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

Метрика делает управление лучше, хотя, и это полезно в себе иногда!

Ответ 12

Это одно из преимуществ TDD: код действует как тест для тестов.

Возможно, вы сделаете эквивалентные ошибки, но это необычно в моем опыте.

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

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

Мне очень нравится описание Боба Мартина как эквивалент двойной записи.

Ответ 13

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

Ответ 14

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

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

Ответ 15

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

Ответ 16

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

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