Интеграционное тестирование (в отличие от Unit- или модульного тестирования) это тестирование не отдельных атомарных компонентов системы (классов) а результата их взаимодействия между собой в какой-либо среде.
Волею судеб я занимаюсь разработкой своего рода интерфейсного фреймворка заточенного на определенные корпоративные нужды. Среда исполнения фреймворка — браузер, а по сему язык — JavaScript.
О том, как можно Unit-тестировать JavaScript я писал ранее, сейчас же расскажу о процессе интеграционного тестирования, применяемого в команде.
С давних времен известен инструмент тестирования веб-приложений/страниц в браузере — Selenium. В плане его применения есть два основных пути, а именно:
WebDriver это новая «фишка» Selenium, появившаяся во второй ветке продукта. Основная его суть — можно гонять тесты, описанные в коде (C#, Java, Python, Ruby), в разных браузерах и/или в виртуальной среде исполнения.
Selenium WebDriver это набор «биндингов» к разным языкам (C#, Java), позволяющий отдавать различные команды «подчиненному» браузеру.
Для каждого браузера имеется своя реализация WebDriver (FireFoxDriver, InternetExplorerDriver, ChromeDriver — сейчас включены в поставку, OperaSoftware разработали OperaDriver). Существует также «виртуальный» HtmlUnitDriver. В отличии от «браузерных» реализаций он не требует установленного браузера и за счет этого работает быстрее и платформонезависим, но есть и минусы — HtmlUnitDriver имеет «свою» реализацию JavaScript и потому поведение «богатых» веб-приложений может в нем отличаться. Для своих задач мы используем «браузерные» реализации, это позволяет проверить приложение именно в той среде, в которой оно будет исполняться впоследствии.
Кратко общая суть работы с WebDriver может быть описана так:
Ниже рассматривается «браузерная» реализация, суть расширение класса RemoteWebDriver (реализует интерфейс WebDriver).
C «найденными» элементами (интерфейс WebElement)
В качестве языка для написания тестов была выбрана Java. Среда для исполнения — JUnit4.
DISCLAIMER: Не претендую на звание крутого джависта, посему если старшие коллеги найдут огрехи и всякие прочие «антипаттерны» — с удовольствием выслушаю в комментариях.
Базовый абстрактный класс веб-тестов.
Конкретный класс с набором тестов (для простоты убраны некоторые проверки, например на то, что элемент по CSS-селектору действительно найден и доступен на странице)
Все тесты запускаются с помощью отдельного таска Ant-билда:
Данный таск прогонит все известные тесты из классов, чьи имена начинаются с Test под браузерами Firefox и InternetExplorer. В зависимостях таски с базовой инициализацией, компиляцией и выгрузкой скомпилированных тестов на тестовую площадку.
Некоторые «браузерные» реализации (Firefox, Opera, Chrome) поддерживают снятие скриншотов. Это может быть полезно дабы зафиксировать визуальное состояние, в котором пребывала тестовая страница в момент, когда тест не прошел. Для этого подойдет функционал JUnit4 — TestWatchman.
Добавим переменную с путем к папке со скриншотами в Ant-билд
В текущей реализации Ant-билд гоняется через Jetbrains TeamCity. Запуск билда настроен на сброс кода в SVN. Интеграционные тесты — часть общей процедуры тестирования. При провале любого из интеграционных тестов снимается скриншот и публикуется как «артефакт» билда — можно видеть не только какие тесты «отъехали» после сброса в транк какого-либо функционала, но и увидеть «как» они «отъехали».
В настоящее время используется тестирование под IE и Firefox, Chrome не подключен по причине некоторых трудностей с интеграцией (судя по всему в ChromeDriver присутствуют некоторые ошибки, не позволяющие нормально искать элементы на странице в некоторых случаях — по состоянию на 2.0b1, сейчас доступна 2.0b2 но работу с ней пока не пробовали)
Волею судеб я занимаюсь разработкой своего рода интерфейсного фреймворка заточенного на определенные корпоративные нужды. Среда исполнения фреймворка — браузер, а по сему язык — JavaScript.
О том, как можно Unit-тестировать JavaScript я писал ранее, сейчас же расскажу о процессе интеграционного тестирования, применяемого в команде.
Selenium
С давних времен известен инструмент тестирования веб-приложений/страниц в браузере — Selenium. В плане его применения есть два основных пути, а именно:
- написание TestSuite в SeleniumIDE и прогон их через SeleniumTestRunner, или
- использование WebDriver
WebDriver это новая «фишка» Selenium, появившаяся во второй ветке продукта. Основная его суть — можно гонять тесты, описанные в коде (C#, Java, Python, Ruby), в разных браузерах и/или в виртуальной среде исполнения.
WebDriver
Selenium WebDriver это набор «биндингов» к разным языкам (C#, Java), позволяющий отдавать различные команды «подчиненному» браузеру.
Для каждого браузера имеется своя реализация WebDriver (FireFoxDriver, InternetExplorerDriver, ChromeDriver — сейчас включены в поставку, OperaSoftware разработали OperaDriver). Существует также «виртуальный» HtmlUnitDriver. В отличии от «браузерных» реализаций он не требует установленного браузера и за счет этого работает быстрее и платформонезависим, но есть и минусы — HtmlUnitDriver имеет «свою» реализацию JavaScript и потому поведение «богатых» веб-приложений может в нем отличаться. Для своих задач мы используем «браузерные» реализации, это позволяет проверить приложение именно в той среде, в которой оно будет исполняться впоследствии.
Кратко общая суть работы с WebDriver может быть описана так:
- реализуется код, использующий какую-либо имплементацию WebDriver. Данный код выполняет какие-либо действия с веб-страницей и сравнивает результат с эталонным
- WebDriver транслирует команды в запущенный браузер (при использовании «браузерной» реализации) и сообщает результаты «обратно в код»
Что умеет WebDriver
Ниже рассматривается «браузерная» реализация, суть расширение класса RemoteWebDriver (реализует интерфейс WebDriver).
- поиск элементов: findElement(s)By*
- CssSelector
- ClassName
- Id
- LinkText
- TagName
- XPath
- загрузка страницы, получение контента страницы
- исполнение произвольного JavaScript
- осуществление операций Drag-n-Drop
C «найденными» элементами (интерфейс WebElement)
- получение текста (text)
- получение значения (value)
- click по элементу
- ввод с клавиатуры (клавиша, сочетание клавиш, последовательность клавиш/сочетаний)
Среда исполнения тестов
В качестве языка для написания тестов была выбрана Java. Среда для исполнения — JUnit4.
DISCLAIMER: Не претендую на звание крутого джависта, посему если старшие коллеги найдут огрехи и всякие прочие «антипаттерны» — с удовольствием выслушаю в комментариях.
Базовый абстрактный класс веб-тестов.
@Ignore abstract public class AbstractWebTest { protected static RemoteWebDriver _driver; // расположение тестовой страницы private String testPageLocation = String.format( "http://%s:%s/test.html", System.getProperty("test.httproot"), // Web-сервер ... System.getProperty("test.httpport", "80") // и порт ); // Используемая имплементация WebDriver private static String driverName = System.getProperty( "test.driver", "org.openqa.selenium.firefox.FirefoxDriver"); /** * Перед каждым набором тестов - создаем инстанс драйвера. * Это автоматически запустит браузер */ @BeforeClass public static void setUpDriver() throws ClassNotFoundException, IllegalAccessException, InstantiationException { _driver = (RemoteWebDriver) Class.forName(driverName).newInstance(); } /** * Перед каждым тестом - открываем тестовую страницу */ @Before public void setUp() { _driver.get(testPageLocation); } /** * После каждого набора тестов - закрываем инстанс дарйвера (закрываем браузер) */ @AfterClass public static void tearDown() { _driver.close(); } }
Конкретный класс с набором тестов (для простоты убраны некоторые проверки, например на то, что элемент по CSS-селектору действительно найден и доступен на странице)
public class TestMoneyField extends AbstractWebTest { /** * При рендеринге поле ввода денежной суммы должно показать 0.00 */ @Test public void testRendering() { WebElement content = _driver.findElementByCssSelector("#FieldMoney .input-text-field"); Assert.assertEquals("0.00", content.getValue()); } /** * Проверим форматирование "триад" */ @Test public void testInputWithoutDot() { WebElement content = _driver.findElementByCssSelector("#FieldMoney .input-text-field"); content.sendKeys("999999"); Assert.assertEquals("999 999.00", content.getValue()); } }
Все тесты запускаются с помощью отдельного таска Ant-билда:
<target name="integrationtest" depends="init, buildtests, deploytests"> <junit haltonfailure="false"> <sysproperty key="test.driver" value="org.openqa.selenium.firefox.FirefoxDriver" /> <classpath> <pathelement location="${path.to.tests.jar}"/> </classpath> <batchtest> <fileset dir="${path.to.compiled.test.classes}"> <include name="**/tests/Test*.class" /> </fileset> </batchtest> </junit> <junit haltonfailure="false"> <sysproperty key="test.driver" value="org.openqa.selenium.ie.InternetExplorerDriver" /> <classpath> <pathelement location="${path.to.tests.jar}"/> </classpath> <batchtest> <fileset dir="${path.to.compiled.test.classes}"> <include name="**/tests/Test*.class" /> </fileset> </batchtest> </junit> </target>
Данный таск прогонит все известные тесты из классов, чьи имена начинаются с Test под браузерами Firefox и InternetExplorer. В зависимостях таски с базовой инициализацией, компиляцией и выгрузкой скомпилированных тестов на тестовую площадку.
Фишки-плюшки
Некоторые «браузерные» реализации (Firefox, Opera, Chrome) поддерживают снятие скриншотов. Это может быть полезно дабы зафиксировать визуальное состояние, в котором пребывала тестовая страница в момент, когда тест не прошел. Для этого подойдет функционал JUnit4 — TestWatchman.
@Ignore abstract public class AbstractWebTest { // Папка для скриншотов private String screenshotDir = System.getProperty("test.screenshotDir", ""); @Rule public MethodRule watchman = new TestWatchman() { /** * Будет вызван при каждом "проваленном" тесте * @param e Брошенное тестом исключение * @param method Тест-метод */ @Override public void failed(Throwable e, FrameworkMethod method) { if(_driver instanceof TakesScreenshot && !screenshotDir.equals("")) { String browserName = _driver.getClass().getName(); String testSuiteName = method.getMethod().getDeclaringClass().getName(); browserName = browserName.substring(browserName.lastIndexOf('.') + 1); testSuiteName = testSuiteName.substring(testSuiteName.lastIndexOf('.') + 1); byte[] screenshot = ((TakesScreenshot)_driver).getScreenshotAs(OutputType.BYTES); try { FileOutputStream stream = new FileOutputStream( new File( String.format("%s/screenshot_%s_%s_%s.png", screenshotDir, browserName, testSuiteName, method.getName()))); stream.write(screenshot); stream.close(); } catch (IOException e1) { e1.printStackTrace(System.out); } } } }; // все остальное... }
Добавим переменную с путем к папке со скриншотами в Ant-билд
<junit haltonfailure="false"> <sysproperty key="test.driver" value="org.openqa.selenium.firefox.FirefoxDriver" /> <sysproperty key="test.screenshotDir" value="${screenshotsDir}" /> <classpath> <pathelement location="${path.to.tests.jar}"/> </classpath> <batchtest> <fileset dir="${path.to.compiled.test.classes}"> <include name="**/tests/Test*.class" /> </fileset> </batchtest> </junit>
Интеграция
В текущей реализации Ant-билд гоняется через Jetbrains TeamCity. Запуск билда настроен на сброс кода в SVN. Интеграционные тесты — часть общей процедуры тестирования. При провале любого из интеграционных тестов снимается скриншот и публикуется как «артефакт» билда — можно видеть не только какие тесты «отъехали» после сброса в транк какого-либо функционала, но и увидеть «как» они «отъехали».
В настоящее время используется тестирование под IE и Firefox, Chrome не подключен по причине некоторых трудностей с интеграцией (судя по всему в ChromeDriver присутствуют некоторые ошибки, не позволяющие нормально искать элементы на странице в некоторых случаях — по состоянию на 2.0b1, сейчас доступна 2.0b2 но работу с ней пока не пробовали)
