Автоматизированное интеграционное тестирование ASP.NET приложенияИсточник: habrahabr habrahabr
В этой статье я хочу поделиться опытом создания инфраструктуры для интеграционного тестирования веб приложения. Приложение построено на платформе .Net и состоит из ASP.NET MVC приложения и базы данных на MSSQL Задача интеграционного тестирования формулировалась следующим образом: автоматизировать развёртывание приложения и выполнение тестов пользовательского интерфейса, чтобы можно было быстро убедиться в том, что устанавливаемая версия приложения успешно отрабатывает все необходимые тестовые сценарии. Другими словами надо быстро проверить, что будет, когда мы установим новую версию заказчику и начнём с ней работать. Поскольку, результат выполнения этих тестов является показателем качества создаваемого приложения, мы всегда будем знать качество нашего приложения, а значит и ситуацию в которой мы находимся. Поскольку интеграционное тестирование позволят имитировать действия пользователя можно сказать, что оно позволят проверять факт того, что такой-то пункт ТЗ успешно выполнен. Если создать тесты для каждого пункта ТЗ (то получим программу и методику испытаний - ПМИ :) и автоматизировать их, то количество успешно выполненных тестов будет означать реальную информацию о том, на сколько процентов исполнено ТЗ. Иначе оценка состояния системы будет выглядеть следующим образом: Что должно проверяться при таком тестировании: Этот процесс показан на диаграмме ниже.
Основные конфигурацииНиже представлено, как должны быть развёрнуты конфигурации на разных средах при интеграционном тестировании и промышленном использовании. Хочу сразу обратить внимание, что взаимодействие с приложением во всех средах происходит одинаково. Взаимодействие с приложением происходит через браузер, обновление базы данных происходит в виде выполнения скрипта обновления БД, обновление ASP.NET приложения происходит через стандартный Deployment.
Конфигурация на машине разработчика
В этой конфигурации предполагается использовать СУБД на машине разработчика. Я считаю этот вариант лучше варианта, когда все разработчики используют одну общую базу данных. С одной стороны общая база данных дешевле в поддержке и у каждого разработчика всегда последняя версия базы данных. Но с другой стороны в таких базах данных можно легко получить нарушение целостности данных, например можно ввести несколько неправильных заказов, потому, что разработчик забыл сделать какую-то проверку. Проверку он, конечно, потом сделает и больше неправильных заказов в системе создаваться не будет. Но другой разработчик может продолжить отлаживать свой функционал на неправильных заказах и наделать много скрытых ошибок. В случае персональной базы данных, задача создания целостных данных становится явной и проще решаемой. Об этом написано ниже в разделе "Маленькие хитрости" Перед интеграционным тестом разработчик должен восстановить рабочую базу данных с промышленного сервера на СУБД интеграционного тестирования. Конечно, рабочую базу получить с промышленного стенда нереально, но все равно должна быть какая-то база данных максимально близкая к ней, чтобы правильно проверить процесс обновления базы данных. В качестве варианта может быть обезличивание рабочей базы данных, то есть делается копия рабочей базы данных, а потом в ней меняются имена заказчиков и суммы контрактов, так чтобы коммерческая информация не попала к разработчикам и тестировщикам. Первым этапом тестов будет проверка развёртывания базы, то есть разработчик будет выполнять публикацию базы данных. Для этого разработчик публикует Database Project в целевую базу данных. Во время сборки проекта Visual Studio залезет в целевую базу, сравнит её со скриптами текущего проекта и сделает разностный скрипт, который и будет выполняться. Уже здесь могут возникнуть ошибки, например, забыли создать поле, на которое ссылается вторичный ключ, или при вставке начальных данных произошёл конфликт первичных ключей. Получается, что разработчик обнаружит и исправит все эти ошибки ещё на этапе тестирования а не на этапе внедрения, думаю все знают сколько это нервов экономит. Следующим шагом разработчик запускает тесты через Visual Studio Test Runner. Интеграционные тесты при выполнении наполняют базу данных требуемыми для теста данными, и тем самым удовлетворяют предварительным требованиям тестов, потом тест, через Selenium RC Server вызывает браузер и имитирует действия пользователя. Результат выполнения теста проверяется опять таки через браузер, так же можно получить данные из БД для сравнения с эталонными. После выполнения теста база данных возвращается в исходное состояние. В результате, после прогона тестов, видно общее состояние системы.
Конфигурация на сервере Continues integration
Конфигурация на промышленной среде
Необходимые инструментыДля создания необходимой инфраструктуры нужны следующие инструменты: Visual Studio Solution, который содержит несколько проектов: Это минимальный набор проектов, который необходим для организации автоматизированного интеграционного тестирования. Конечно, Solution хранится в системе контроля версий, например SVN. Помимо этого solution содержит nuGet пакет "Selenuim Remote Contol". К сожалению Selenim IDE не полностью поддерживает новый "Selenium WebDriver", поэтому экспортировать тестовые сценарии из Selenim IDE в C# код можно только для Remote Contol. В противном случае часть шагов не будет экспортирована в C# и их придётся писать руками, что заметно медленнее чем через экспорт из Selenim IDE. Для "Selenuim Remote Contol" нужно java приложение "selenium-server-standalone-x.x.x.jar" Для быстрой и удобной записи тестов понадобится FireFox с установленным дополнением Selenim IDE. Так же нужен Continues Integration сервер, для постоянного получения последней версии из SVN и выполнения тестов.
Настройка средыПокажу как устанавливать и конфигурировать описанные выше продукты на различных стендах.
проект базы данныхНачнем с проекта базы данных. Database project может иметь несколько publish.xml файлов в каждом из которых описано куда и как будет публиковаться база данных. Поскольку база данных у каждого разработчика находится по своему адресу, эти файлы должны быть вынесены из системы контроля версий и настраиваться у каждого разработчика отдельно. Чтобы опубликовать базу данных надо кликнуть правой кнопкой на этом файле в Visual Studio и выбрать "Publish". Публикация аналогична развёртыванию (Deploy) за исключением того, что параметры развёртывания хранятся в свойствах проекта, а параметры публикации в publish.xml. Чтобы опубликовать БД из командной строки надо вызвать такую команду:
Веб приложениеASP.NET приложение в Visual Studio можно запускать по команде "Run" или же развёртывать стандартными средствами Visual Studio. На Continues Integration Server-е это можно сделать тоже различными способами, например, через xCopy. Я не буду останавливаться на этом подробнее, потому что это, с одной стороны достаточно большой материал, а с другой стороны его легко найти в интернете.
Selenium ServerТак же понадобится Selenium RC сервер, который можно скачать здесь: docs.seleniumhq.org/download/ Запускает сервер командой Когда сервер запущен можно прогонять тесты.
Тестовый проект с интеграционными тестамиЭто может быть MSTest проект или NUnit проект. Как уже говорилось в начале каждого теста надо прогнать SQL скрипт, который наполнит БД данными для теста. Поскольку я часто работаю со скриптам созданными в SQL Server Management Studio, то в них постоянно встречается команда "GO". Чтобы выполнять такие скрипты используется библиотека smo, это несколько сборок, которые можно установить в составе MSSQL Server SDK и найти например в "C:\Program Files (x86)\Microsoft SQL Server\110\SDK\Assemblies", но надо учесть, что они собраны для .Net 2.0 и просто так заставить их работать в .Net 4.0 и старше не получиться. Поэтому тестовое приложение, я обычно конфигурирую для .Net 3.5 После этого можно сделать такой вспомогательный класс, в тестовом проекте:
Все SQL скрипты лежат у меня в этом же тестовом проекте в папке "SQL", и для каждого скрипта свойство "Copy to Output directory" выставлено в "Copy if newer" Что находится в скриптах "BackupBeforeTest.sql" и "RestoreAfterTest.sql" написано ниже. В результате тест выглядит примерно следующим образом:
Этот простой тест проверяет вход пользователя в систему. К сожалению не получается полностью изолировать друг от друга интеграционные тесты, в частности средствами Seleniun нельзя удалить "ASP.NET_Session" cookie из бразузера, потому что она помечена как HTTPOnly. Из-за этого приходится проверять остался пользователь залогинен с прошлого теста или нет, и если остался то приходится имитировать выход пользователя из системы, и таким образом сбрасывать состояние сессии. Остальной ход теста достаточно очевиден. Вводится логин и пароль и проверяется, что сообщения об ошибке нет, и появилась ссылка для выхода из системы. Как написать такой тест с помощью Selenium IDE смотри ниже.
FireFox и Selenium IDE. Создание тестовSelenium позволяет прогонять тесты на многих браузерах, но для быстрого создания надо использовать FireFox с установленным дополнением "Selenium IDE". Чем же так хороша эта "Selenium IDE"?
На рисунке показан примет теста, который проверят изменение записи в справочнике. Эта страница реализована с помощью DevExpress MVC Extension и содержит много AJAX кода поэтому добавлены шаги вроде "WaitFor...", которые как раз и ожидают появления в браузере необходимых обновлений. Так же большим достоинством является то, что тест можно быстро отлаживать через "Selenium IDE". Достаточно нажать кнопку "Run Test" и проверить, что все работает. После того как тест отлажен его можно сохранить в c# файл, командой "Export Test Sute As" / "C# / NUnit / Remote Control", и скопировать содержимое тестовых методов в тестовый проект. Все это значительно повышает скорость создания тестов по сравнению с вариантом, когда вы сразу пишете тест в C#, и отлаживаете его прогоняя через MSTest или NUnit. А самым замечательным свойством "Selenium IDE" я считаю тот факт, что эти тесты могут писать тестировщики для своих целей и если какой-то тест очень важен, то его можно передать разработчикам и они добавят его в список интеграционных тестов. В результате получается, что тестировщики помогают писать тесты, хотя конечно не стоит забывать, что тестировщики не могут писать SQL скрипты, которые подготовят базу данных для теста, а это существенная часть теста.
Маленькие хитрости
Создание и загрузка данных теста в БДДанные для теста нужны что бы привести систему к состоянию описанному в предусловии к тесту. Например, должен быть покупатель разместивший три заказа. Легче всего создать покупателя и заказы через пользовательский интерфейс. Дальше надо выгрузить данные из требуемых таблиц в SQL скрипты в виде SQL Dump-а. И этот скрипт потом использовать при прогоне теста. Пример такого скрипта:
Данный скрипт можно получить через программы вроде "SQL Dumper" или через SQL Server Management Studio, у которого есть хороший мастер для генерации скриптов по объектам БД, включая данные таблиц. -[Управление идентификаторами записей в БД]- Думаю вы уже обратили внимание, что идентификаторы, в скриптах создания данных, начинаются с 1000000, а не с 1. Чтобы этого избежать надо в разработческих и тестовых базах данных сразу поставить начальное значение для генератора идентификаторов в миллион или даже в миллиард.
Очистка базы данных после тестаСамым простым и эффективным способом могло бы быть использование транзакций, но в нашей конфигурации тест и приложение выполняются в разных процессах и не получается легко использовать одну транзакции в обоих процессах. поэтому перед каждым тестом я создаю backup скриптом из файла "BackupBeforeTest.sql"
а после теста восстанавливаю следующим скриптом из "RestoreAfterTest.sql":
Как видно перед восстановлением надо отключить всех пользователей, которые остались подключены после выполнение теста. Иначе не получиться восстановить базу данных.
Организация SQL скриптов для инкрементного наполнения БД перед тестомЯ несколько раз упоминал, что перед выполнением скрипта надо подготовить базу данных для него. Но если копнуть поглубже, то будет видно, что эти скрипты имеют зависимости друг от друга. Начнём с нулевого уровня: справочные данные создаются ещё в рамках первоначального развёртывания базы данных, на эти справочные данные ссылаются многие сущности в системе. Дальше идут пользователи, на них тоже много кто ссылается. В качестве дальнейших примеров, можно взять каталог товаров, и заказы, которые ссылаются на товары, пользователей и справочники. Очевидно, что подготавливая тестовые данные для заказа разумно воспользоваться уже существующими данным для создания пользователей и каталога товаров, это как минимум сэкономит время. Но на самом деле здесь есть ещё больше достоинств. Вспомним пример, который я упоминал выше, когда один разработчик, например работающий над каталогом товаров, забыл реализовать какое-то правило, и насоздавал несколько неправильных данных, в том числе и тестовых. Другой разработчик, который занимается заказами, сделал свой код используя неправильные данные о товарах. Этот принцип работает и в другую сторону. Если разработчику заказов понадобились новые тестовые данные для товаров, которых ещё нет, то он должен сам подготовить для себя такие данные и прогнать свой тест заказа на этом новом товаре. Но это так же значит, что этот новый товар должен успешно пройти все тесты, связанные с каталогом товаров. Если какой-то тест, в каталоге товаров, не проходит с новым товаром, это значит, что разработчик заказа обнаружил ошибку в коде разработчика каталога товаров. Опять, таки получается, что ошибки в каталоге товаров обнаруживаются раньше, по сравнению с тем случаем, когда каждый разработчик готовит себе все тестовые данные.
ЗаключениеОписанный способ организации автоматизированного интеграционного тестирования, позволяет автоматически оценивать качество создаваемого Веб приложения на всем этапе разработки, а не только на этапе стабилизации. Чем раньше обнаружены ошибки, тем дешевле их исправить, а главное сразу, а не только на стабилизации, виден объем оставшихся работ, что позволяет менеджерам проектов точнее планировать работы. Конечно, поддержание описанной инфраструктуры требует определённых затрат, но я считаю, то эти затраты окупаются за счёт меньших затрат на ручное тестирование. |