Что делает хороший Unit Test?

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

Мой вопрос: вы следуете любым правилам поведения для написания тестов, чтобы избежать проблем в будущем? Чтобы быть более конкретным: каковы свойства хороших модульных тестов или как вы пишете свои тесты?

Рекомендуются предложения по языку для агностиков.

Ответ 1

Позвольте мне начать с подключения источников - Прагматическое тестирование модуля в Java с помощью JUnit (Там версия с С# -Nunit тоже.. но я есть этот.. его агностик по большей части. Рекомендуем.)

Хорошие тесты должны быть TRIP (акроним не достаточно липкий). У меня есть распечатка текста в книге, которую я должен был вытащить, чтобы убедиться, что я прав. )

  • Автоматически. Вызов тестов, а также проверка результатов для PASS/FAIL должны быть автоматическими.
  • Тщательный: Покрытие; Хотя ошибки, как правило, группируются вокруг определенных регионов в коде, убедитесь, что вы проверяете все ключевые пути и сценарии. Используйте инструменты, если вам нужно знать непроверенные области.
  • Повторяемый. Тесты должны давать одинаковые результаты каждый раз... каждый раз. Тесты не должны полагаться на неконтролируемые параметры.
  • Независимый: Очень важно.
    • Тесты должны проверять только одну вещь за раз. Несколько утверждений в порядке, если они все тестируют одну функцию/поведение. Когда тест не удался, он должен определить местоположение проблемы.
    • Тесты не должны полагаться друг на друга - изолированы. Нет предположений о порядке выполнения теста. Обеспечьте "чистый шифер" перед каждым тестом, используя правильную настройку/разборку.
  • Профессиональный. В конечном итоге у вас будет такой же тестовый код, как и производство (если не больше), поэтому следуйте тому же стандарту хорошего дизайна для вашего тестового кода. Хорошо укомплектованные методы-классы с раскрывающимися намерениями именами, без дублирования, тесты с хорошими именами и т.д.

  • Хорошие тесты также работают Быстро. любой тест, который занимает более половины секунды для запуска, должен быть обработан. Чем дольше набор тестов будет работать, тем реже он будет запущен. Чем больше изменений, разработчик попытается прокрасться между прогонами.. если что-то сломается.. потребуется больше времени, чтобы выяснить, какое изменение было виновником.

Обновление 2010-08:

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

Кроме того, большинство других - это руководящие принципы, которые ограничивают работу с низкой выгодой: например, "Не проверяйте код, который у вас нет" (например, сторонние библиотеки DLL). Не ходите на тестирование геттеров и сеттеров. Следите за соотношением цены и прибыли или вероятности дефекта.

Ответ 2

  • Не записывайте ginormous тесты. Как показывает "unit" в "unit test", сделайте каждый из них как можно более атомарным и изолированным. Если необходимо, создайте предварительные условия с использованием макетных объектов, вместо того, чтобы вручную создавать слишком много типичной пользовательской среды.
  • Не тестируйте вещи, которые, очевидно, работают. Избегайте тестирования классов у стороннего поставщика, особенно тех, которые предоставляют основные API-интерфейсы фреймворка, в который вы вставляете код. Например, не проверяйте добавление элемента в класс Hashtable поставщика.
  • Рассмотрите возможность использования инструмента покрытия кода, такого как NCover, чтобы помочь обнаружить случаи кросс, которые вы еще не тестировали.
  • Попробуйте написать тест перед реализацией. Подумайте о тестировании как о большей спецификации, которую придерживается ваша реализация. Ср также поведенческая разработка, более конкретная ветвь тестового развития.
  • Будьте последовательны. Если вы только пишете тесты для некоторых своих кодов, это вряд ли полезно. Если вы работаете в команде, а некоторые или все другие не пишут тесты, это тоже не очень полезно. Убедите себя и всех остальных в важности (и экономии времени) тестирования, или не беспокойтесь.

Ответ 3

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

Womp 5 Законы письменности:


1. Используйте длинные имена описательных методов тестирования.

   - Map_DefaultConstructorShouldCreateEmptyGisMap()
   - ShouldAlwaysDelegateXMLCorrectlyToTheCustomHandlers()
   - Dog_Object_Should_Eat_Homework_Object_When_Hungry()

2. Напишите свои тесты в Стиль Arrange/Act/Assert.

  • Хотя эта организационная стратегия существует некоторое время и назвал многие вещи, введение акронима "ААА" в последнее время было отличным способом добиться этого. Согласование всех ваших тестов с Стиль AAA облегчает их чтение и поддерживать.

3. Всегда указывайте сообщение об ошибке с помощью своих утверждений.

Assert.That(x == 2 && y == 2, "An incorrect number of begin/end element 
processing events was raised by the XElementSerializer");
  • Простая, но полезная практика, которая делает очевидным в вашем приложении для бегунов то, что не удалось. Если вы не предоставляете сообщение, вы обычно получаете что-то вроде "Ожидаемое истинное, ложное" в выводе сбоя, что заставляет вас действительно читать тест, чтобы узнать, что не так.

4. Комментировать причину теста - что такое бизнес-предположение?

  /// A layer cannot be constructed with a null gisLayer, as every function 
  /// in the Layer class assumes that a valid gisLayer is present.
  [Test]
  public void ShouldNotAllowConstructionWithANullGisLayer()
  {
  }
  • Это может показаться очевидным, но это практика защитит целостность ваших тестов от людей, которые не понять причину теста в первую очередь. Я видел много тесты удаляются или изменяются, были совершенно прекрасны, просто потому, что человек не понимал предположения, что тест был проверки.
  • Если тест тривиален или метод имя достаточно наглядно, оно может быть разрешено покинуть комментарий выключен.

5. Каждый тест должен всегда возвращать состояние любого ресурса, к которому он прикасается

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

Ответ 4

Помните эти цели (адаптированные из книги xUnit Test Patterns by Meszaros)

  • Тесты должны снизить риск, а не ввести его.
  • Тесты должны быть легко запущены.
  • Тесты должны быть просты в обслуживании. система развивается вокруг них

Некоторые вещи, чтобы сделать это проще:

  • Тесты должны только терпеть неудачу из-за одна причина.
  • Тесты должны проверять только одну вещь.
  • Минимизировать тестовые зависимости (нет зависимости от баз данных, файлов, ui и др.)

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

Ответ 5

Некоторые свойства больших модульных тестов:

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

  • Когда вы рефакторинг, тесты не должны терпеть неудачу.

  • Тесты должны выполняться так быстро, что вы никогда не стесняетесь их запускать.

  • Все тесты должны проходить всегда; нет недетерминированных результатов.

  • Модульные тесты должны быть хорошо учтены, как и ваш производственный код.

@Alotor: Если вы предлагаете, чтобы библиотека имела только модульные тесты в своем внешнем API, я не согласен. Я хочу модульные тесты для каждого класса, включая классы, которые я не выставляю внешним абонентам. (Тем не менее, если я чувствую необходимость писать тесты для частных методов, тогда мне нужно рефакторинг.


EDIT: Был комментарий о дублировании, вызванном "одним утверждением за тест". В частности, если у вас есть код для настройки сценария, а затем вы хотите сделать несколько утверждений об этом, но только одно утверждение для каждого теста, вы можете дублировать настройку на нескольких тестах.

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

[TestFixture]
public class StackTests
{
    [TestFixture]
    public class EmptyTests
    {
        Stack<int> _stack;

        [TestSetup]
        public void TestSetup()
        {
            _stack = new Stack<int>();
        }

        [TestMethod]
        [ExpectedException (typeof(Exception))]
        public void PopFails()
        {
            _stack.Pop();
        }

        [TestMethod]
        public void IsEmpty()
        {
            Assert(_stack.IsEmpty());
        }
    }

    [TestFixture]
    public class PushedOneTests
    {
        Stack<int> _stack;

        [TestSetup]
        public void TestSetup()
        {
            _stack = new Stack<int>();
            _stack.Push(7);
        }

        // Tests for one item on the stack...
    }
}

Ответ 6

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

Ответ 7

То, что вам нужно, - это определение поведения тестируемого класса.

  • Проверка ожидаемого поведения.
  • Проверка ошибок.
  • Охват всех путей кода внутри класса.
  • Выполнение всех функций-членов внутри класса.

Основное намерение - увеличить уверенность в поведении класса.

Это особенно полезно при анализе кода. У Мартина Фаулера есть интересная статья статьи относительно тестирования на его веб-сайте.

НТН.

веселит,

Rob

Ответ 8

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

Ответ 9

Мне нравится аббревиатура Right BICEP из вышеупомянутого Прагматическое тестирование модулей:

  • Правильно. Являются ли результаты правильными?
  • B: Правильны ли все условия b?
  • I. Можем ли мы проверить обратные отношения i?
  • C. Можем ли мы c проверять результаты поиска с помощью других средств?
  • E: Можем ли мы принудительно выполнить условия e rror?
  • P: Являются p характеристиками эргономики в пределах границ?

Лично я чувствую, что вы можете получить довольно далеко, проверяя, что вы получаете правильные результаты (1 + 1 должен возвращать 2 в функции добавления), проверяя все граничные условия, о которых вы можете думать (например, используя два числа сумма которого больше целочисленного максимального значения в функции добавления) и вызывая условия ошибки, такие как сбои сети.

Ответ 10

Хорошие тесты нуждаются в ремонте.

Я не совсем понял, как это сделать для сложных сред.

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

  • Взаимодействие команды взрывается
  • количество тестовых случаев взрывается
  • Взаимодействие между компонентами взрывается.
  • время для создания всех unittests становится значительной частью времени сборки
  • Изменение API может пульсировать сотнями тестовых случаев. Несмотря на то, что изменение производственного кода было легким.
  • увеличивается количество событий, необходимых для последовательности процессов в правильном состоянии, что, в свою очередь, увеличивает время выполнения теста.

Хорошая архитектура может контролировать некоторый взрыв взаимодействия, но неизбежно, как системы становятся более сложными, а автоматизированная система тестирования растет вместе с ней.

Здесь вы начинаете иметь дело с компромиссами:

  • проверять только внешний API, иначе внутренняя обработка рефакторинга приводит к существенному пересмотру тестового теста.
  • Настройка и отключение каждого теста усложняется, поскольку инкапсулированная подсистема сохраняет больше состояний.
  • ночная компиляция и автоматическое выполнение теста увеличиваются до нескольких часов.
  • увеличение времени компиляции и выполнения означает, что дизайнеры не выполняют или не будут запускать все тесты
  • чтобы сократить время выполнения теста, вы считаете, что тесты последовательности выполняются, чтобы уменьшить настройку и отключение.

Вам также нужно решить:

Где вы храните тестовые примеры в своей базе кода?

  • как вы документируете свои тестовые примеры?
  • можно проверить, чтобы светильники были повторно использованы для сохранения обслуживания тестового случая?
  • что происходит, когда выполнение ночного теста не выполняется? Кто делает сортировку?
  • Как вы поддерживаете макет объектов? Если у вас есть 20 модулей, все из которых используют свой собственный дизайн API-интерфейсов для фальсификации, это быстро изменяет ряды API. Изменения не только меняются, но и 20 макетных объектов меняются. Эти 20 модулей были написаны в течение нескольких лет многими различными командами. Его классическая проблема повторного использования.
  • люди и их команды понимают ценность автоматических тестов, которые им просто не нравятся, как это делает другая команда.: -)

Я мог бы продолжать вечно, но я хочу сказать, что

Тесты нуждаются в ремонте.

Ответ 11

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

Способ определения "хороших" модульных тестов - это если они обладают следующими тремя свойствами:

  • Они читаемы (имена, утверждения, переменные, длина, сложность).
  • Они поддаются контролю (нет логики, а не над указанными, основанными на состоянии, реорганизованными..)
  • Они заслуживают доверия (проверяйте правильные вещи, изолируйте, а не тесты интеграции).

Ответ 12

  • Единичное тестирование просто проверяет внешний API вашего модуля, вы не должны тестировать внутреннее поведение.
  • Каждый тест TestCase должен тестировать один (и только один) метод внутри этого API.
    • В случае сбоя должны быть включены дополнительные тестовые примеры.
  • Проверьте охват ваших тестов: после проверки устройства, 100% строк внутри этого устройства должны были быть выполнены.

Ответ 13

Jay Fields имеет много хороших советов о написании модульных тестов и должность, где он обобщает наиболее важные советы. Там вы прочтете, что вы должны критически думать о своем контексте и судить, стоит ли вам совет. Здесь вы получаете массу удивительных ответов, но зависит от вас, что лучше для вашего контекста. Попробуйте их и просто рефакторинг, если он плохо пахнет вам.

С уважением

Ответ 14

Никогда не предполагайте, что будет работать тривиальный 2-строчный метод. Написание быстрого unit test - это единственный способ предотвратить пропущенный нулевой тест, неправильный знак минуса и/или тонкую ошибку охвата от кусания вас, неизбежно, когда у вас есть еще меньше времени для решения проблемы, чем сейчас.

Ответ 15

Во второй ответ "A TRIP", за исключением того, что тесты ДОЛЖНЫ полагаться друг на друга!!!

Почему?

DRY - Dont Repeat Yourself - также относится к тестированию! Тестовые зависимости могут помочь: 1) сохранить время настройки, 2) сохранить ресурсы прибора и 3) точно определить неисправности. Конечно, только учитывая, что ваша среда тестирования поддерживает первоклассные зависимости. В противном случае я признаю, что они плохие.

Последующие действия http://www.iam.unibe.ch/~scg/Research/JExample/

Ответ 16

Часто модульные тесты основаны на макет-объекте или макет данных. Мне нравится писать три типа модульных тестов:

  • "переходные" модульные тесты: они создают свои собственные макетные объекты/данные и проверяют их функции с ним, но уничтожают все и не оставляют следов (например, никаких данных в тестовой базе данных).
  • "persistent" unit test: они тестируют функции внутри вашего кода, создавая объекты/данные, которые будут необходимы более продвинутой функции позже для их собственного unit test (избегая для этой расширенной функции воссоздавать каждый раз, когда их собственный набор макет объектов/данных)
  • "постоянные" модульные тесты: модульные тесты с использованием макетных объектов/данных, которые уже существуют (потому что созданы в другом сеансе unit test) с помощью постоянных модульных тестов.

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

  • Я запускаю третий вид очень часто, потому что все макетные объекты/данные уже существуют.
  • Я запускаю второй тип всякий раз, когда меняет модель.
  • Я запускаю первый, чтобы проверять основные функции время от времени, чтобы проверить основные регрессии.

Ответ 17

Подумайте о двух типах тестирования и рассмотрите их по-разному - функциональное тестирование и тестирование производительности.

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

Ответ 18

Я использую согласованное соглашение об именовании тестов, описанное Roy Osherove Unit Test Стандарты именования. Каждый метод в данном классе тестового случая имеет следующее стиль именования MethodUnderTest_Scenario_ExpectedResult.

  • Первое имя тестового имени - это имя метода в тестируемой системе.
  • Далее идет конкретный сценарий, который тестируется.
  • Наконец, это результат этого сценария.

В каждом разделе используется верхний чехол для верблюда и разделяется под балл.

Я нашел это полезным, когда я запускаю тест, тест сгруппирован по имени тестируемого метода. И соглашение позволяет другим разработчикам понять намерение тестирования.

Я также добавляю параметры к имени метода, если тестируемый метод перегружен.