Что такое модульное тестирование?

Я видел много вопросов, спрашивающих «как» проводить модульное тестирование на определенном языке, но ни одного вопроса, спрашивающего «что», «почему» и «когда».

  • Что это?
  • Что это значит для меня?
  • Зачем мне его использовать?
  • Когда мне следует его использовать (а когда нет)?
  • Каковы некоторые распространенные ошибки и заблуждения

Ответов (20)

Решение

Модульное тестирование - это, грубо говоря, тестирование фрагментов вашего кода изолированно с тестовым кодом. На ум сразу приходят следующие преимущества:

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

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

Другой способ взглянуть на модульное тестирование - это сначала написать тесты. Это называется разработкой через тестирование (сокращенно TDD). TDD дает дополнительные преимущества:

  • Вы не пишете спекулятивный код «Мне это может понадобиться в будущем» - ровно столько, чтобы тесты прошли
  • Написанный вами код всегда покрывается тестами
  • Написав сначала тест, вы вынуждены думать о том, как вы хотите вызвать код, что обычно улучшает дизайн кода в долгосрочной перспективе.

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

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


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

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

  1. TDD НЕ означает написание вдвое большего количества кода. Тестовый код, как правило, довольно быстро и безболезненно писать, и он является ключевой частью вашего процесса проектирования и критически.

  2. TDD помогает понять, когда следует прекратить кодирование! Ваши тесты вселяют уверенность в том, что вы уже сделали достаточно и можете перестать настраивать и перейти к следующему.

  3. Тесты и код работают вместе, чтобы добиться лучшего кода. Ваш код может быть плохим / ошибочным. Ваш ТЕСТ может быть плохим / ошибочным. В TDD вы делаете ставку на то, что вероятность того, что ОБА плохая / глючная, довольно низка. Часто это тест, который требует исправления, но это все равно хороший результат.

  4. TDD помогает при кодировании запоров. Вы знаете это чувство, что у вас так много дел, что вы даже не знаете, с чего начать? Сегодня пятница, если вы просто откладываете на потом еще пару часов ... TDD позволяет вам очень быстро конкретизировать то, что, по вашему мнению, вам нужно сделать, и ускоряет процесс кодирования. Кроме того, как лабораторные крысы, я думаю, что все мы реагируем на этот большой зеленый свет и усердно работаем, чтобы увидеть его снова!

  5. Подобным образом эти типы дизайнеров могут ВИДЕТЬ, над чем они работают. Они могут уйти, чтобы перекусить соком / сигаретой / айфоном, и вернуться к монитору, который сразу же даст им визуальную подсказку о том, куда они пришли. TDD дает нам нечто подобное. Когда жизнь вмешивается, легче увидеть, куда мы попали ...

  6. Я думаю, что это Фаулер сказал: «Несовершенные тесты, которые выполняются часто, намного лучше, чем идеальные тесты, которые вообще никогда не пишутся». Я интерпретирую это как разрешение писать тесты, которые, по моему мнению, будут наиболее полезными, даже если остальная часть моего кода будет ужасно неполной.

  7. TDD помогает во всевозможных неожиданных ситуациях. Хорошие модульные тесты могут помочь документировать, что что-то должно делать, они могут помочь вам перенести код из одного проекта в другой и дать вам необоснованное чувство превосходства над вашими коллегами, не тестирующими :)

Эта презентация - отличное введение во все, что влечет за собой тестирование вкусностей.

В университете меня никогда не учили модульному тестированию, и мне потребовалось время, чтобы его «освоить». Я прочитал об этом, сказал: «А, да, автоматическое тестирование, это могло бы быть круто, я думаю», а потом забыл об этом.

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

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

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

Основное отличие модульного тестирования от «просто открытия нового проекта и тестирования этого конкретного кода» заключается в том, что оно автоматизировано , а значит, воспроизводимо .

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

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

Это главное преимущество модульных тестов перед ручным. Но подождите, есть еще кое-что:

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

Фреймворки модульного тестирования, в свою очередь, упрощают написание и выполнение тестов.

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

Это ответ на вопрос, почему вам следует проводить модульное тестирование.


Три видео ниже посвящены модульному тестированию в javascript, но общие принципы применимы для большинства языков.

Модульное тестирование: минуты сейчас сэкономят часы - Эрик Манн - https://www.youtube.com/watch?v=_UmmaPe8Bzc

Модульное тестирование JS (очень хорошо) - https://www.youtube.com/watch?v=-IYqgx8JxlU

Написание тестируемого JavaScript - https://www.youtube.com/watch?v=OzjogCFO4Zo


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

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


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

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


Это довольно сбивает с толку, когда вы впервые слышите об этом предмете и думаете про себя: разве я еще не тестирую свой код? И код, который вы написали, работает так, как и предполагалось, «зачем мне нужен другой фреймворк?» ... Да, вы уже тестируете свой код, но компьютер лучше справляется с этим. Вам просто нужно один раз написать достаточно хорошие тесты для функции / единицы кода, а об остальном позаботится мощный процессор вместо того, чтобы вручную проверять, что весь ваш код все еще работает, когда вы вносите изменения в ваш код.

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

Я не не согласен с Дэном (хотя, возможно, лучше просто не отвечать) ... но ...

Модульное тестирование - это процесс написания кода для проверки поведения и функциональности вашей системы.

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

  1. Упростите изменение технической реализации, убедившись, что вы не меняете поведение (рефакторинг). Правильно протестированный код можно подвергнуть агрессивному рефакторингу / очистке с небольшими шансами сломать что-либо, не заметив этого.
  2. Дайте разработчикам уверенность при добавлении поведения или внесении исправлений.
  3. Задокументируйте свой код
  4. Укажите области вашего кода, которые тесно связаны. Трудно модульный тестовый код, который тесно связан
  5. Предоставьте средства для использования вашего API и устраните трудности на ранней стадии.
  6. Обозначает методы и классы, которые не очень связаны

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

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

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

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

Это мой взгляд на это. Я бы сказал, что модульное тестирование - это практика написания тестов программного обеспечения для проверки того, что ваше реальное программное обеспечение выполняет то, для чего оно предназначено. Это началось с jUnit в мире Java и стало лучшей практикой в ​​PHP, а также с SimpleTest и phpUnit . Это основная практика экстремального программирования, которая помогает вам быть уверенным, что ваше программное обеспечение по-прежнему работает должным образом после редактирования. Если у вас достаточно тестового покрытия, вы можете быстро провести серьезный рефакторинг, исправить ошибки или добавить функции, не опасаясь возникновения других проблем.

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

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

Фреймворк выполнит все тесты вашего кода, а затем сообщит об успешном или неудачном завершении каждого теста. По умолчанию phpUnit запускается из командной строки Linux, хотя для него доступны HTTP-интерфейсы. SimpleTest по своей природе основан на веб-технологиях, и его намного проще установить и запустить, IMO. В сочетании с xDebug phpUnit может предоставить автоматическую статистику покрытия кода, что некоторые люди считают очень полезными.

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

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

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

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

Таким образом, модульный тест будет реализовывать функциональность, заключенную в «функции», которую вы тестируете, без побочных эффектов обновления базы данных.

Допустим, ваша функция извлекла некоторые числа из базы данных, а затем выполнила расчет стандартного отклонения. Что вы здесь пытаетесь проверить? Стандартное отклонение рассчитывается правильно или данные возвращаются из базы данных?

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

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

Как тогда мы можем проводить модульное тестирование?

Вы начинаете с малого. В проекте, в который я только что вошел, не было модульного тестирования несколько месяцев назад. Когда покрытие было таким низким, мы просто выбирали файл без покрытия и нажимали «добавить тесты».

Сейчас у нас более 40%, и нам удалось собрать большую часть низко висящих фруктов.

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

Я пошел на презентацию модульного тестирования на FoxForward 2007, и мне сказали никогда не тестировать модульное тестирование всего, что работает с данными. В конце концов, если вы тестируете данные в реальном времени, результаты непредсказуемы, а если вы не тестируете данные в реальном времени, вы фактически не тестируете код, который вы написали. К сожалению, сейчас я пишу большую часть кода. :-)

Недавно я попробовал TDD, когда писал процедуру для сохранения и восстановления настроек. Во-первых, я убедился, что могу создать объект хранилища. Затем у него был метод, который мне нужно было вызвать. Тогда я мог бы это назвать. Затем, чтобы я мог передать ему параметры. Затем, чтобы я мог передать ему определенные параметры. И так далее, пока я, наконец, не убедился, что он сохранит указанный параметр, позволил мне изменить его, а затем восстановить его для нескольких разных синтаксисов.

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

Разработка через тестирование как бы переняла термин «модульное тестирование». Как старый таймер я упомяну его более общее определение.

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

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

Используйте Testivus . Все, что вам нужно знать, прямо здесь :)

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

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

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

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

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

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

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

Я хотел бы порекомендовать книгу xUnit Testing Patterns Джерарда Месароша. Это большой, но отличный ресурс по модульному тестированию. Вот ссылка на его веб-сайт, где он обсуждает основы модульного тестирования. http://xunitpatterns.com/XUnitBasics.html

Я думаю, что вы не понимаете, что фреймворки для модульного тестирования, такие как NUnit (и подобные), помогут вам в автоматизации тестов малого и среднего размера. Обычно вы можете запускать тесты в графическом интерфейсе (например, в случае с NUnit ), просто нажав кнопку, а затем, надеюсь, увидеть, что индикатор выполнения остается зеленым. Если он станет красным, фреймворк покажет вам, какой тест не прошел и что именно пошло не так. В обычном модульном тесте вы часто используете утверждения, например Assert.AreEqual(expectedValue, actualValue, "some description") - поэтому, если два значения не равны, вы увидите ошибку с сообщением «некоторое описание: ожидалось <expectedValue>, но было <actualValue>».

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

Модульное тестирование - это написание кода, который тестирует код вашего приложения.

Часть имени Unit указывает на намерение тестировать небольшие блоки кода (например, один метод) за раз.

xUnit поможет с этим тестированием - они помогают в этом. Частично это автоматизированные программы запуска тестов, которые сообщают вам, какой тест не прошел, а какой прошел.

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

У вас может быть тест, чтобы проверить, что было сгенерировано ожидаемое исключение, без необходимости самостоятельно писать весь блок try catch.

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

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

xUnit , NUnit , mbUnit и т. д. - это инструменты, которые помогут вам в написании тестов.

Библиотеки, такие как NUnit , xUnit или JUnit , просто обязательны, если вы хотите разрабатывать свои проекты с использованием подхода TDD, популяризированного Кентом Беком:

Вы можете прочитать Введение в разработку через тестирование (TDD) или книгу Кента Бека Разработка через тестирование: на примере» .

Затем, если вы хотите быть уверены, что ваши тесты охватывают «хорошую» часть вашего кода, вы можете использовать такое программное обеспечение, как NCover , JCover , PartCover или что-то еще. Они сообщат вам процент покрытия вашего кода. В зависимости от того, насколько вы разбираетесь в TDD, вы будете знать, достаточно ли вы его практиковали :)