Как проверить тесты?

53

Мы проверяем наш код, чтобы сделать его более правильным (на самом деле, менее вероятно, что он будет неправильным ). Тем не менее, тесты также являются кодом - они также могут содержать ошибки. И если ваши тесты содержат ошибки, они вряд ли улучшат ваш код.

Я могу думать о трех возможных типах ошибок в тестах:

  1. Логические ошибки, когда программист неправильно понял поставленную задачу, и тесты делают то, что, как он думал, должны делать, что неправильно;

  2. Ошибки в базовой структуре тестирования (например, утечка насмешливой абстракции);

  3. Ошибки в тестах: тест немного отличается от того, что думает программист.

Ошибки типа (1), кажется, невозможно предотвратить (если программист просто ... не станет умнее). Тем не менее, (2) и (3) могут быть гибкими. Как вы справляетесь с этими типами ошибок? Есть ли у вас какие-то особые стратегии, чтобы их избежать? Например, вы пишете какие-то специальные «пустые» тесты, которые проверяют только предположения автора теста? Кроме того, как вы подходите к отладке неработающего контрольного примера?

Рышард Сзопа
источник
3
Кажется, что каждая вводная часть, которую я читал о насмешках, решает эту проблему. Как только вы начинаете издеваться над чем-то, тесты всегда кажутся более сложными, чем код, который они тестируют. Очевидно, что это менее вероятно при тестировании реального кода, но это очень обескураживает, когда вы пытаетесь учиться.
Carson63000
@ Carson63000 Если это простой тестовый тест с чем-то проверенным , сложность делится и контролируется (я думаю).
mlvljr
13
Но тогда как вы тестируете тестовые тесты?
ocodo
+1. Пункт 1 может быть ошибкой требований. Может быть предотвращено только путем рассмотрения требований. Вероятно, из рук программиста, если только они не являются аналитиком требований
MarkJ
@ocodo: так же, как вы смотрите Наблюдатели. :)
Грег Бургхардт

Ответы:

18

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

  1. Есть несколько методов, которые не позволяют вам добавлять одну и ту же ошибку в код и тесты:

    1. Клиент должен быть человеком, отличным от исполнителя.
    2. Сначала напишите тесты, а затем код (как в Test Driven Development).
  2. Вам не нужно тестировать базовую платформу. Тесты не только проверяют код, написанный вами, но и запускают код с платформы. Хотя вам необязательно хотеть обнаруживать ошибки в платформе тестирования, очень сложно писать код и тесты, которые всегда скрывают ошибку в платформе, другими словами, очень трудно иметь систематическую ошибку как в ваших тестах / коде, так и в коде. в платформе, и вероятность уменьшается с каждым тестом, который вы создаете. Даже если вы попытаетесь сделать это, у вас будет очень сложная задача.

  3. У вас могут быть ошибки в тестах, но обычно они легко обнаруживаются, потому что тесты проверяются разработанным кодом. Между кодом и тестами у вас есть обратная связь. Оба предсказывают, как должен вести себя конкретный вызов интерфейса. Если ответ отличается, вам не обязательно иметь ошибку в коде. У вас также может быть ошибка в тесте.

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

Попробуйте сделать отдельные тесты как можно меньше (короткими).

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

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

доктор Ганнибал Лектер
источник
3
Что, если тесты нуждаются в довольно сложной настройке? Иногда такие вещи не находятся под вашим контролем.
Рышард Сзопа
Ну, я предполагаю, что сложные настройки являются «начальным условием» тестов. Если это не удается, все ваши тесты должны провалиться. На самом деле, я сейчас работаю над таким проектом, и люди, которые никогда не использовали модульные тесты, постоянно спрашивали одно и то же ... пока мы не объяснили, что такое юнит-тесты :) Потом они поняли, что это можно сделать, несмотря на огромные усилия. сложность проекта.
доктор Ганнибал Лектер
Каков наилучший способ проверить, что это «начальное условие» выполнено, именно в этом и заключается мой вопрос. Вы пишете отдельный тест для этого? Или просто предположите, что тесты сломаются, если это условие не соответствует действительности? А как насчет ситуации, когда настройка не «катастрофически» плоха, а просто слегка отключена?
Рышард Сзопа
2
Ваши тесты должны провалиться, если начальные условия не верны, вот и весь смысл. Когда в состоянии A, вы ожидаете результата B. Если у вас нет состояния A, тест не пройден. В этот момент вы можете выяснить, почему это не удалось, плохие начальные условия или плохой тест, но в обоих случаях он должен потерпеть неудачу. Даже если это так, как вы говорите, «немного от» (то есть "A" => "B", "a" => "b", но никогда не "a" => "B"или вашего теста плохо).
доктор Ганнибал Лектер
19

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

Более исчерпывающим способом проверки качества набора тестов является использование мутационного тестирования .

Дон Роби
источник
2
И что ваш тест не пройден по правильной причине .
Фрэнк Шиарар
@Frank - Да. Я добавлю это к ответу.
Дон Роби
И вы добавляете новый тест для нового поведения, которое будет протестировано. Не добавляйте в существующие тесты.
Гуперникетес
@ DonRoby, Вы нашли тестирование мутаций полезным на практике? Какие недостатки вы обнаружили в своих тестовых случаях с этим?
dzieciou
4

Для № 1 и № 3: модульные тесты не должны содержать никакой логики; если вы это сделаете, то, вероятно, вы тестируете более одной вещи в своем модульном тесте. Один из лучших методов для юнит-тестирования - это иметь только один тест на юнит-тест.

Посмотрите это видео Роя Ошерова, чтобы узнать больше о том, как правильно писать модульные тесты.

Пирс Майерс
источник
Объявление № 3 - Я согласен, что тесты должны быть максимально простыми и не должны содержать никакой логики. Однако, подумайте о фазе настройки теста, когда вы создаете объекты, которые ему понадобятся. Вы можете создавать слегка неправильные объекты. Это те проблемы, о которых я думаю.
Рышард Сзопа
Когда вы говорите «слегка неправильные объекты», подразумеваете ли вы, что состояние объекта не является правильным или фактический дизайн объекта не является правильным? Для состояния объекта вы можете написать тесты для проверки его правильности. Если конструкция ошибочна, то тест не пройден.
Пирс Майерс
3

С точки зрения # 1 - я думаю, что это хорошая идея для анализа пар / кода для этой стороны вещей. Легко сделать предположение или просто ошибиться, но если вам нужно объяснить, что делает ваш тест, в чем смысл, вы, скорее всего, поймете, если нацеливаетесь не на ту цель.

Сэм Дж
источник
2

Должен быть момент, когда нужно прекратить попытки юнит-тестирования. Должен знать, когда проводить черту. Должны ли мы писать контрольные примеры для проверки контрольных примеров? А как насчет новых тестовых случаев, написанных для тестовых случаев? Как мы их проверим?

if (0 > printf("Hello, world\n")) {
  printf("Printing \"Hello, world\" failed\n");
}

Изменить: Обновлено с объяснением, как предлагается в комментарии.

aufather
источник
-1 Что? Это, кажется, не имеет никакого отношения.
альтернатива
2
Должен быть момент, когда нужно прекратить попытки юнит-тестирования. Должен знать, когда проводить черту. Должны ли мы писать контрольные примеры для проверки контрольных примеров? А как насчет новых тестовых случаев, написанных для тестовых случаев? Как мы их проверим?
aufather
2
Процесс Мозг поднял EInfiniteRecursion, пытаясь экстраполировать ваше заявление ...
Мейсон Уилер
Замените свой ответ своим комментарием, и вы получите +1
Примечание для себя - придумайте имя
3
Справедливости ради, ваш пример соломенный человек. Вы тестируете подсистему printf () в библиотеке C, а не реальную программу, вызывающую printf (). Однако я согласен с тем, что в какой-то момент нужно прервать рекурсивное тестирование тестов.
Тим Пост
2

Привет.
Вы должны к приложениям:

  • Ваш продукт
  • Ваш тест для этого продукта.

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

  • приложение работает не так, как вы ожидали (это ожидание выражается в вашем тесте)
  • приложение работает правильно, вы просто не задокументировали это поведение правильно (в ваших тестах)

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

Тесты проверяют только некоторые части приложения. Я тестирую приложение, я тестирую тесты.

yoosiba
источник
2

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

Код, который вы пишете, реализует набор спецификаций. (Если X, то Y, если только Z в этом случае Q и т. Д. И т. Д.). Все, что должен пытаться выполнить тест, - это определить, что X на самом деле является Y, если только в этом случае не будет Z. Это означает, что все, что должен делать тест, это установить X и проверить Y.

Но это не все случаи, вы, вероятно, говорите, и вы были бы правы. Но если вы сделаете тест достаточно «умным», чтобы знать, что X должен быть только на Y, если не на Z, тогда вы в основном повторно реализуете бизнес-логику в тесте. Это проблематично по причинам, о которых мы поговорим чуть ниже. Вы не должны улучшать покрытие кода, делая свой первый тест «умнее», вместо этого вы должны добавить второй тупой тест, который устанавливает X и Z и проверяет Q. Таким образом, у вас будет два теста, один, который охватывает общий случай ( иногда также известный как «счастливый путь» и тот, который охватывает крайний случай в качестве отдельного теста.

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

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

TL: DR: Если ваши тесты нуждаются в тестировании, вы делаете это неправильно. Пишите тупые тесты .

GordonM
источник
0

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

CMR
источник
0

Краткий ответ: производственный код тестирует тесты .

Сравните это с моделью кредита / дебета, используемой в экономике. Механика очень проста - если кредит отличается от дебета, значит что-то не так.

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

Обратите внимание, что ваши ошибки типа (1) не могут быть найдены в модульных тестах. Чтобы избежать этого типа ошибок, вам нужны другие инструменты.

vidstige
источник