Вход |  Регистрация

Все Тэги

Модульное тестирование

06.08.20132077 просм.

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

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

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

В Консо, более традиционной из двух компаний, работники с опаской относятся к быстрым циклам Аджайл. Здесь принято поставлять высококачественный продукт, и к тестированию относятся очень серьезно. Большинство тестов выполняются вручную, каждое действие пользователя воспроизводится следующим образом: нажать кнопку, здесь ввести текст, там посмотреть, что вывелось на экран. Такое не повторишь каждую ночь  В Консо с трудом представляют, как Аджайл выпускает обновления так часто и, в тоже время, тестирует их должным образом.

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

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

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

Поэтому, хотя можно много сказать хорошего о ручном тестировании с Visual Studio, мы начнем со знакомства с модульным тестированием. Модульное тестирование – это самый простой тип автоматизированных тестов; их можно запускать на компьютере автономно используя Visual Studio.

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

В данной серии заметок мы расскажем о следующем:

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

Необходимые программы

Для того чтобы выполнять модульные тесты, понадобится установить Visual Studio.

Правила, согласно которым пишутся и выполняются тесты, определяются фреймфорком для модульного тестирования. К Visual Studio прилагается MSTest, поэтому в примерах будет использован он.

Также для модульного тестирования можно использовать и другой фреймворк. Например, NUnit, Visual Studio 2012, так же легко определит и запустит тесты через универсальный пользовательский интерфейс. Visual Studio даже может выполнять их вместе с тестами написанными для MSTest и других фреймворков. (Для работы не с MSTest в Visual Studio 2010 нужны дополнения, и интеграция не такая хорошая как в Visual Studio 2012.)

В следующих заметках мы поговорим о внесении тестов и кода в хранилище, и выполнении тестов на сервисе сборки.

Модульное тестирование с Visual Studio

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

Для создания теста MSTest в Visual Studio, нужно открыть меню Test, выбрать New Test , следовать подсказкам. Будет создан проект тестирования и каркас для кода теста. Пример (C#):

 

Каждый тест представлен одним тестируемым методом. Тестовых методов и классов можно создать сколько угодно и называть их по своему усмотрению. Каждый метод с атрибутом [TestMethod] будет вызван фреймворком модульного тестирования. Также можно добавлять другие методы, которые будут вызываться тестовыми методами.

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

Выполнение модульных тестов

Модульные тесты можно выполнять напрямую из Visual Studio, и они будут (по умолчанию) выполняться на компьютере, где их запускают. (Больше информации можно найти в MSDN теме Выполнение Модульных тестов в Обозревателе Модульных Тестов) При нажатии CTRL+R, A будет произведена сборка и запуск модульных тестов. Результаты будут выведены в окне Test Explorer в Visual Studio:

Результаты модульного теста

(Интерфейс пользователя отличен в Visual Studio 2010, но суть та же.)

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

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

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

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

Отладка модульных тестов

При выполнении Run All, тесты выполняются в обычном режиме (не в отладочном). Это предпочтительнее, так как тесты выполняются быстрее, не замедляя процесса разработки.

Как бы там ни было, когда тест проваливается, можно выбрать опцию Debug Selected Tests. Важно помнить, что тесты могут выполняться в любом порядке.

Разработка тестов до написания кода

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

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

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

Юлия и Ларс дают свои комментарии. Морт правит тест, и создает еще один, чтобы показать другие аспекты поведения, которое они ожидают. Его коллеги могут быть уверенны в том, что знают, что будет делать метод Морта и могут начать писать свой код. В каком-то смысле, тесты образуют контракт между создателем и пользователями компонента.

Подсказка:
Проще представить модульный тест как пример использования метода, который еще не написан.

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

Ограничения разработки тестов до написания кода?

В большом количестве случаев test-first разработка очень полезна. В частности, при разработке интерфейса программного приложения (API) и элементов рабочего процесса, где входные и выходные данные четко определены. Но в других случаях они могут казаться не такими полезными. Например, при проверке точности текста сообщения об ошибке. Неужели кто-нибудь будет делать проверки типа:

 

Тесты на основе контрольных данных

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

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

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

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

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

 

Но постойте…

Подсказка:
Подумайте о фактической информации о результате, который стремитесь получить. И запишите это в виде теста.

Что можно сделать, так это оценить несколько отдельных обязательных свойств результата:

 

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

Как пользоваться модульными тестами

В дополнение к test-first разработке (или, по крайней мере, тестированию как можно раньше), мы рекомендуем:

  • Задача по разработке не может считаться выполненной до прохождения всех модульных тестов.
  • Следует ожидать, что время на написание модульных тестов и самого кода будет примерно одинаковым. Усилия вознаграждаются тем, что код будет более надёжным, с меньшим количество ошибок и потребуется меньше усилий на переписывание кода.
  • Модульные тесты представляют собой требования для тестируемого модуля. (Мы не имеем в виду требования к приложению в целом. Просто требования к модулю, который может быть чем угодно, от отдельного метода до важной подсистемы).
  • Разделяйте требования на отдельные пункты. Например:
    • Возвращаемое значение умноженное на себя же должно равняться входному значению И
    • В случае если входящее значение – отрицательное число, должно выбрасывать исключение И ….
  • Пишите отдельные модульные тесты для каждого пункта, также как писал отдельные тесты Морт. Как следствие, тесты будут более гибкими, и их будет легче менять, когда требования поменяются.
  • Работая над кодом старайтесь одновременно иметь дело с небольшим количеством отдельных тестов.
  • Не нужно менять или удалять модульный тест, кроме как в том случае, если соответствующее требование поменялось. Или если было замечено, что тест неправильно отражает какое-то отдельное требование.

Метки: , , ,

Добавить комментарий

Для отправки комментария вам необходимо авторизоваться.

Партнеры DevOpsHub и DevOpsWiki