|
|
|||||||||||||||||||||||||||||
|
В погоне за качеством кода: Программное тестирование с помощью Selenium и TestNG (исходники)Источник: IBM developerWorks Россия Эндрю Гловер
Selenium - это каркас для Web-тестирования, реализующий новый подход к валидации Web-приложений. В отличие от большинства инструментов для Web-тестирования, которые пытаются симулировать HTTP запросы, Selenium подходит к Web-тестированию так, как если бы он сам был браузером. При запуске автоматического теста Selenium, каркас запускает браузер и действительно проводит браузер через все шаги, намеченные в тесте, точно так же, как бы это делал пользователь, взаимодействуя с приложением. Selenium также отличается от других каркасов для тестирования приложений тем, что позволяет легко писать тесты как программистам, так и непрограммистам. В Selenium тесты можно писать как программно, так и используя Fit-таблицы, а написанные тесты его можно полностью автоматизировать. Можно, например, легко запустить весь набор тестов Selenium с помощью ANT, а можно запускать тесты Selenium в рамках среды постоянной интеграции (continuous integration). В этом месяце я представлю Selenium и рассмотрю его функциональность, ставящую ее на особое место среди каркасов для тестирования Web-приложений, особенно в комбинации с TestNG, DbUnit, Cargo и им подобными.
Программное тестирование с SeleniumВ Selenium можно создавать тесты программно с помощью языка по вашему выбору или Fit-таблиц. С точки зрения тестирования процесс и его результаты будут мало различаться, какой бы подход вы ни выбрали. Меня здесь интересует исследование программного подхода к Selenium, так как он дает интересные возможности в сочетании с TestNG. Одно из преимуществ программного использования Selenium с каркасом, подобным TestNG, в том, что он позволяет создавать интеллектуальные фикстуры, что сложно сделать с помощью Fit-таблиц. TestNG особенно хорошо сочетается с Selenium, так как он может делать вещи, невозможные в других технологиях, такие как тестирование с использованием зависимостей, перезапуск неудачных тестов и создание параметризованных тестов с параметрами, определенными в отдельных файлах. Иметь все эти было бы замечательно для любой среды тестирования Web-приложений, но еще лучше, как вы увидите, иметь возможность использовать их для полностью автоматизированного тестирования на соответствие требованиям пользователя. Настройка первого тестаАрхитектура Selenium в действительности состоит из двух логических сущностей: проверочный код, написанный вами, и сервер Selenium, который помогает взаимодействовать с тестируемым приложением. Для успешного выполнения тестов необходимо наличие работающих экземпляров сервера Selenium и тестируемого приложения. Результат тестов, конечно, будет зависеть от того, насколько хорошо написано приложение. К счастью, сервер Selenium - это нетребовательный к ресурсам процесс, который можно запускать и останавливать программно в рамках теста. Запуск и остановка сервера Selenium, реализованного как объект Чтобы программно запустить сервер Selenium, нужно создать новый объект Selenium и сказать ему, какой совместимый браузер нужно использовать; для данного примера использовался Firefox. Также нужно указать, где запущен экземпляр сервера (обычно это В листинге 1 я настроил локальный экземпляр Листинг 1. Конфигурация сервера Selenium
Когда объект Прохождение приложенияПрограммное взаимодействие с Web-страницами - это упражнение по использование логических идентификаторов. Некоторые читатели уже знакомы с этой концепцией по февральской статье о TestNG-Abbot. Первый шаг по взаимодействию с элементом страницы - это найти его, для чего обычно используются идентификаторы ID HTML элементов. Selenium также позволяет по вашему желанию использовать для поиска конкретных элементов XPath, регулярные выражения и даже JavaScript. HTML в листинге 2 - это часть простого Web-приложения, использующего Groovlet'ы. В коде определена одна форма, содержащая поле ввода и кнопку подтверждения формы. Чтобы использовать Selenium для взаимодействия с этой формой, нужно предоставить ID для поля ввода и соответствующее значение для него. Также нужно предоставить ID для кнопки подтверждения, чтобы Selenium смог нажать её. После нажатия форма отправляется на Groovlet, в данном случае
Теперь можно программно взаимодействовать с HTML формой, используя ID Листинг 3. Проход простой Web-страницы
API-интерфейс Selenium для взаимодействия с элементами Web-страниц довольно интуитивен. Для полей ввода можно использовать метод
|
@Parameters({"selen-svr-addr","brwsr-path","aut-addr"}) @BeforeClass private void init(String selenSrvrAddr, String bpath, String appPath) throws Exception { driver = new DefaultSelenium(selenSrvrAddr, SeleniumServer.getDefaultPort(), bpath, appPath); driver.start(); } //.... @AfterClass private void stop() throws Exception { driver.stop(); } |
Необходимо связать названия параметров со значениями в файле testing.xml TestNG, так что я объявляю три элемента parameter, как показано в листинге 5. По умолчанию параметр brwsr-path
ведет к Firefox, но можно с легкостью определить новый набор тестов с использованием Internet Explorer.
Листинг 5. Значения параметров в файле TestNG testing.xml
<parameter name="selen-svr-addr" value="localhost"/> <parameter name="aut-addr" value="http://localhost:8080/gt15/"/> <parameter name="brwsr-path" value="*firefox"/> |
Затем определяется сценарий тестирования, показанный в листинге 6, который также принимает параметр с базовым URL к тестируемому приложении. Этот тест заставляет браузер открыть конкретную страницу внутри Web-приложения и провести ряд действий над формой, показанной на рисунке 1.
Листинг 6. Стандартный сценарий тестирования
@Parameters({"aut-addr"}) @Test public void verifyCreate(String appPath) throws Exception { driver.open(appPath + "/CreateWidget.html"); driver.type("widget", "book-01"); driver.select("type", "book"); driver.type("definition", "book widget type book"); driver.click("submit"); driver.waitForPageToLoad("10000"); assertEquals(driver.getText("success"), "The widget book-01 was successfully created.", "test didn't return expected message"); } |
После того как форма была подтверждена через метод driver.click("submit")
, я заставляю Selenium дождаться загрузки ответа и затем проверяю наличие сообщения об успешном создании. Обратите внимание, что Web-страница с ответом имеет элемент с ID success .
Сведя все это вместе, получаем настраиваемый вариант тестирования, проверяющий два сценария: стандартный путь и пограничное состояние, когда отсутствует значение поля definition, как показано в листинге 7.
Листинг 7. Использование TestNG
public class CreateWidgetUATest { private Selenium driver; @Parameters({"selen-svr-addr","brwsr-path","aut-addr"}) @BeforeClass private void init(String selenSrvrAddr, String bpath, String appPath) throws Exception { driver = new DefaultSelenium(selenSrvrAddr, SeleniumServer.getDefaultPort(), bpath, appPath); driver.start(); } @Parameters({"aut-addr"}) @Test public void verifyCreate(String appPath) throws Exception { driver.open(appPath + "/CreateWidget.html"); driver.type("widget", "book-01"); driver.select("type", "book"); driver.type("definition", "book widget type book"); driver.click("submit"); driver.waitForPageToLoad("10000"); assertEquals(driver.getText("success"), "The widget book-01 was successfully created.", "test didn't return expected message"); } @Parameters({"aut-addr"}) @Test public void verifyCreationError(String appPath) throws Exception { driver.open(appPath + "/CreateWidget.html"); driver.type("widget", "book-02"); driver.select("type", "book"); //definition explicitly set to blank driver.type("definition", ""); driver.click("submit"); driver.waitForPageToLoad("10000"); assertEquals(driver.getText("failure"), "There was an error in creating the widget.", "test didn't return expected message"); } @AfterClass private void stop() throws Exception { driver.stop(); } } |
Мы уже определили два теста Selenium, достаточно гибких, чтобы облегчить тестирование с несколькими браузерами и несколькими местоположениями, что уже неплохо для новичка. Переходя на более продвинутый уровень, я хочу подумать о том, воспроизводима ли логика в моих тестах. Например, что произойдет, если запущу класс теста CreateWidgetUATest
два раза подряд. Как я могу быть уверенным, что Web-приложение запущено на последней версии кода на моем локальном компьютере или на любом компьютере, если уж на то пошло.
И сервер Selenium, и проверяемое Web-приложение при запуске тестов Selenium должны работать. Подразумевается, что должны работать и все зависящие компоненты архитектуры; для Java™ Web-приложений это контейнер сервлетов и соответствующая база данных.
Как я объяснял в статье про повторяемые системные тесты, мои любимые технологии для реализации логической воспроизводимости в зависящих от базы данных Web-приложениях - это DbUnit и Cargo. DbUnit управляет данными в базе, а Cargo автоматизирует типовые процессы управления контейнером. В следующих разделах будет показано, как сочетать их с Selenium и TestNG, чтобы обеспечить логическую воспроизводимость тестов на соответствие требованиям пользователя.
Вы, вероятно, помните, что DbUnit облегчает работу с базами данных, управляя данными в них в контексте сценария теста. С помощью DbUnit можно загрузить известный набор данных в базу перед тестом, что позволяет быть уверенным в наличии этих данных во время тестирования. Кроме того, можно удалить из базы любые данные, созданные в ходе теста, после его завершения. Технология DbUnit упрощает эти задачи, выступая в качестве удобной фикстуры, пригодной для использования в JUnit или TestNG. DbUnit считывает файлы шаблонов, содержащие тестовые данные, и логически вставляет, удаляет или обновляет эти данные в соответствующих таблицах базы.
Так как я использую TestNG для управления Selenium, то я намерен создать фикстуру DbUnit, которая будет запускаться на уровне теста . TestNG поддерживает исполнение фикстур на пяти уровнях детальности. Нижние два уровня - уровни метода и класса - наиболее понятны, фикстуры предназначены для каждого тестового метода или для целого класса. После них TestNG определяет фикстуру для коллекции тестов, определенных в конфигурационных файлах TestNG и отмеченных элементом test
, фикстуру для целого набора (suite) - коллекции коллекций тестов, отмеченной элементом suite
, и фикстуру для группы тестов, определенных аннотацией Test
из TestNG.
Создание фикстуры DbUnit для запуска на уровне теста означает, что классы тестов из коллекции будут вместе использовать одну и ту же логику для правильного наполнения базы перед запуском любого теста. В нашем случае необходимо, чтобы в базе данных был "чистый" набор данных перед запуском каждой коллекции логических тестов. Используя команду DbUnit CLEAN_INSERT
, можно быть уверенным, что все записи, созданные в предыдущем тесте, будут удалены, так что можно запускать тест, создающий данные, снова и снова, не беспокоясь об ограничениях базы данных.
Более того, я хочу, чтобы моя фикстура использовала параметризованные данные, дающие гибкость в переключении файлов шаблонов и местоположения базы данных, перед запуском каждого отдельного теста. Связать параметры с TestNG очень просто, все, что нужно - это отметить фикстуру аннотацией Parameters
, объявить соответствующие параметры в сигнатуре метода и предоставить значения в конфигурационном файле TestNG.
В листинге 8 приведена простая фикстура DbUnit, заполняющая базу данными из файла шаблона. Отметим, что фикстура определена так, чтобы принимать пять параметров. Возможно, это слишком много, но разве не удобно иметь возможность задавать параметры для фикстуры?
Листинг 8. Фикстура DbUnit для коллекции тестов
public class DatabaseFixture { @Parameters({"seed-path","db-driver","db-url","db-user","db-psswrd"}) @BeforeTest public void seedDatabase(String seedpath, String driver, String url, String user, String pssword) throws Exception { IDatabaseConnection conn = this.getConnection(driver, url, user, pssword); IDataSet data = this.getDataSet(seedpath); try { DatabaseOperation.CLEAN_INSERT.execute(conn, data); }finally { conn.close(); } } private IDataSet getDataSet(String path) throws IOException, DataSetException { return new FlatXmlDataSet(new File(path)); } private IDatabaseConnection getConnection(String driver, String url, String user, String pssword ) throws ClassNotFoundException, SQLException { Class.forName(driver); Connection jdbcConnection = DriverManager.getConnection(url, user, pssword); return new DatabaseConnection(jdbcConnection); } } |
Чтобы привязать реальные значения к параметрам, приведенным в листинге 8, нужно определить их в файле TestNG testng.xml
, как показано в листинге 9:
Листинг 9. Специфические параметры DbUnit, объявленные в файле TestNG testing.xml
<parameter name="seed-path" value="test/conf/gt15-seed.xml"/> <parameter name="db-driver" value="org.hsqldb.jdbcDriver"/> <parameter name="db-url" value="jdbc:hsqldb:hsql://127.0.0.1"/> <parameter name="db-user" value="sa"/> <parameter name="db-psswrd" value=""/> |
Теперь, определив настраиваемую фикстуру, отвечающую за состояние базы и некоторые соответствующие тесты, можно с помощью TestNG связать все вместе. Как обычно, осознание цели - это первый шаг к её достижению. В данном случае нам необходимо достичь следующего:
Хорошо, что элементы parameter
в TestNG имеют локальную область действия. В результате я могу легко определить настраиваемые значения параметров в конфигурационном файле TestNG и затем переопределить их при необходимости в группирующих элементах test
TestNG.
Например, чтобы запустить два набора тестов, я просто создаю два элемента test
. Затем можно подключить фикстуру и сопутствующие тесты через элемент package
TestNG, который облегчает поиск всех тестов (или фикстур) в структуре пакета. Затем можно связать параметр brwsr-path
с Firefox и Internet Explorer в двух определенных группирующих элементах test
. Все это показано в файле testing.xml в листинге 10.
Листинг 10. Настраиваемый файл testing.xml, который запускает фикстуру DbUnit
<suite name="User Acceptance Tests" verbose="1" > <!-- required for DbUnit fixture --> <parameter name="seed-path" value="test/conf/gt15-seed.xml"/> <parameter name="db-driver" value="org.hsqldb.jdbcDriver"/> <parameter name="db-url" value="jdbc:hsqldb:hsql://127.0.0.1"/> <parameter name="db-user" value="sa"/> <parameter name="db-psswrd" value=""/> <!-- required for Selenium fixture --> <parameter name="selen-svr-addr" value="localhost"/> <parameter name="aut-addr" value="http://localhost:8080/gt15/"/> <test name="GT15 CRUDs- Firefox" > <parameter name="brwsr-path" value="*firefox"/> <packages> <package name="test.com.acme.gt15.Web.selenium" /> <package name="test.com.acme.gt15.Web.selenium.fixtures" /> </packages> </test> <test name="GT15 CRUDs- IE" > <parameter name="brwsr-path" value="*iexplore"/> <packages> <package name="test.com.acme.gt15.Web.selenium" /> <package name="test.com.acme.gt15.Web.selenium.fixtures" /> </packages> </test> </suite> |
Теперь можно сказать, что у нас есть практически все необходимое для создания набора повторяемых тестов для проверки требований пользователя. Все что осталось - это научиться управлять самим Web-контейнером. К счастью мы можем использовать Cargo.
Cargo - это инновационный проект с открытым кодом, направленный на разработку общих методов автоматизации управления контейнерами. Например, один и тот же API позволяет установить War-файл на JBoss и может запускать и останавливать Tomcat. Cargo также может автоматически скачивать и устанавливать контейнер. API Cargo можно использовать различными способами - от Java-кода до задач Ant и даже Maven.
Использование инструмента, подобного Cargo, решает одну из основных проблем написания логических воспроизводимых тестовых сценариев - как избежать обычного допущения, что в запущенном контейнере находится последняя и лучшая версия кода приложения. Более того, можно настроить процесс сборки (например, в Ant), который с помощью функциональности Cargo будет автоматически выполнять следующие задачи:
Затем можно использовать Cargo для остановки выбранного контейнера. Более того, нет необходимости беспокоиться о скачивании и установке контейнера - если на локальном компьютере уже установлена правильная версия, то Cargo пропустит шаги 1 и 2.
Я бы хотел использовать Cargo, чтобы убедиться, что используется последняя и лучшая версия моего Web-приложения. Более того, я не хочу беспокоиться о том, куда необходимо установить War-файл, равно как и о том, используется ли самый последний War-файл. Что мне на самом деле нужно - это сделать тестирование на соответствие ожиданиям пользователя неинтерактивным ( non-event ), чтобы можно было ввести одну команду, например, ua-test
, откинуться назад и ждать результатов. Еще лучше то, что в случае процесса с постоянной интеграцией не нужно даже ничего ждать, так как я просто получу извещение после завершения тестов.
Чтобы настроить Cargo из Ant, необходимо определить задачу, скачивающую конкретную версию Tomcat и устанавливающую его во временный каталог на локальном компьютере. Затем последняя версия кода, упакованная в WAR-файл, устанавливается в Tomcat, как показано в листинге 11.
Листинг 11. Задача для настройки Cargo
<target name="ua-test" depends="compile-tests,war"> <taskdef resource="cargo.tasks"> <classpath> <pathelement location="${libdir}/${cargo-jar}" /> <pathelement location="${libdir}/${cargo-ant-jar}" /> </classpath> </taskdef> <cargo containerId="tomcat5x" action="start" wait="false" id="${tomcat-refid}"> <zipurlinstaller installurl="${tomcat-installer-url}" /> <configuration type="standalone" home="${tomcatdir}"> <property name="cargo.remote.username" value="admin" /> <property name="cargo.remote.password" value="" /> <deployable type="war" file="${wardir}/${warfile}" /> </configuration> </cargo> <antcall target="_start-selenium" /> <cargo containerId="tomcat5x" action="stop" refid="${tomcat-refid}" /> </target> |
Задача (target) из листинга 11 через элемент antcall
вызывает другую задачу. По существу, последняя задача cargo
в листинге 11 служит оболочкой для задачи _start_selenium
и гарантирует, что Tomcat будет остановлен после тестов.
В задаче _start_selenium
, определенной в листинге 12, нужно запустить и затем остановить сервер Selenium. Кстати, мои тесты также подключатся к этому серверу на своих фикстурах Selenium. Отметим также, что эта задача ссылается на другую задачу _run-ua-tests
, определенную в листинге 13.
Листинг 12. Запуск и остановка сервера Selenium
<target name="_start-selenium"> <java jar="${libdir}/${selenium-srvr-jar}" fork="true" spawn="true" /> <antcall target="_run-ua-tests" /> <get dest="${testreportdir}/results.txt" src="${selenium-srvr-loc}/selenium-server/driver/?cmd=shutDown" /> </target> |
Последняя задача в группе реально запускает программные тесты Selenium через TestNG. Отметим также, что я заставляю TestNG использовать мой файл testing.xml, указывая xmlfileset
в задаче _run-ua-tests
из листинга 13.
Листинг 13. Запуск тестов, обнаруженных в файле testing.xml TestNG
<target name="_run-ua-tests"> <taskdef classpathref="build.classpath" resource="testngtasks" /> <testng outputDir="${testreportdir}" classpath="${testclassesdir};${classesdir}" haltonfailure="true"> <xmlfileset dir="./test/conf" includes="testng.xml" /> <classpath> <path refid="build.classpath" /> </classpath> </testng> </target> |
Как вы увидели, Selenium значительно облегчает тестирование на соответствие требованиям пользователя, особенно когда он управляется TestNG. Хотя программное тестирование - вещь не для всех, и непрограммисты предпочтут Fit-таблицы Selenium, оно позволяет воспользоваться исключительной гибкостью TestNG. Программное тестирование также позволяет построить свою собственную среду для тестирования с помощью DbUnit и Cargo, чтобы обеспечить логическую воспроизводимость тестов.
Эволюция каркасов тестирования Web-приложений с открытым кодом, конечно еще не завершилась, что наверняка порадует борцов за качество кода. Технология Selenium - один из первых open-source каркасов нового поколения для тестирования Web-приложений с помощью браузера, автоматизирующих тесты на соответствие требованиям пользователя, и уже поэтому - выдающийся. Сочетая Selenium с TestNG, как я показал в этой статье, вы получите отличный тестовый механизм, вместе со значительными преимуществами тестирования зависимостей и параметризованного тестирования. Дайте шанс Selenium и TestNG, и пользователи будут благодарить вас за это.
Главная страница - Программные продукты - Статьи - Разработка ПО, Средства тестирования |
Распечатать »
Правила публикации » |
Написать редактору | |||
Рекомендовать » | Дата публикации: 20.01.2008 | |||
|
Новости по теме |
Российские Android-программисты останутся без денег. Google запрещает переводы
|
Рассылки Subscribe.ru |
Статьи по теме |
Новинки каталога Download |
5 бесплатных приложений, которые будут напоминать вам отдохнуть от экрана компьютера или смартфона
|
Исходники |
Документация |