Интеграционное тестирование (в отличие от 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 но работу с ней пока не пробовали)