Selenium2 ( AKA —WebDriver) является де-факто стандартной технологией веб-тестирования. Хорошо, что Selenium теперь собирает больше сообщений о вакансиях, чем QTP , но это уже другая тема. клиентские MVC — фреймы являются благом для разработки веб-приложений, но как насчет написания тестовых сценариев для готового продукта? В этой записи блога я покажу, насколько тестируемыми являются приложения AngularJS и KnockoutJS . По моему мнению, эти две являются ведущими технологиями MVC на стороне клиента , хотя есть альтернативы, такие как Backbone.js, которые имеют значительную долю рынка. Я предпочитаю два, которые я выделяю над Backbone по ряду причин.
Тестирование приложения KnockoutJS с Selenium2
На сайте сравнения технологий Addy Osmani есть приложение TODO . Он использует Knockout 2.0 (Knockout 2.1 в настоящее время находится на стадии бета-тестирования). Вот как это выглядит:
При тестировании приложения я собираюсь написать для идиоматического приложения Knockout. Вот DOM, как видит его Firebug:
Я выделил «1» — строку ввода, где публикуются новые записи, и «2» — атрибут, в котором Knockout отмечает, что он циклически перебирает элементы todo. Распространены через DOM и многие другие атрибуты привязки данных. Это доказательство того, что Knockout управляет страницей. У HTML нет этого атрибута, его понимает только Knockout. Браузер просто игнорирует атрибуты, которые он не понимает.
Вот скрипт Selenium2 (Groovy), который тестирует это приложение. Он собирается добавить и элемент, затем пометить его как «выполненный», а затем подтвердить, что пользовательский интерфейс отображает его как выполненный с зачеркнутым:
@Grapes([ @Grab("org.seleniumhq.selenium:selenium-java:2.20.0"), @Grab("org.seleniumhq.selenium.fluent:fluent-selenium:1.5.1"), @GrabExclude('xml-apis:xml-apis') ]) import org.openqa.selenium.By import org.openqa.selenium.firefox.FirefoxDriver import org.openqa.selenium.Keys import org.openqa.selenium.StaleElementReferenceException import org.seleniumhq.selenium.fluent.FluentWebDriverImpl import static org.hamcrest.CoreMatchers.equalTo; import static org.junit.Assert.assertThat; import static org.seleniumhq.selenium.fluent.FluentBy.attribute import static org.seleniumhq.selenium.fluent.Period.secs def driver = new FirefoxDriver() def fluent = new FluentWebDriverImpl(driver) driver.get "http://addyosmani.github.com/todomvc/architecture-examples/knockoutjs/index.html" def todo = "abc 123 def 456" def start = System.currentTimeMillis() // there are many angular examples in one page def form = fluent.within(secs(10)).div(By.id("todoapp")) // add an entry form.input(By.xpath("contains(@data-bind, 'enterKey: add')")).sendKeys todo + Keys.RETURN assertThat("no items checked initially", form.lis(By.className("done")).size(), equalTo(0)) def lis = form.ul(attribute("data-bind", "foreach: todos")).lis() // loop through the todo items .. for (li in lis) { if (li.getText().contains(todo)) { // .. click the checkbox for the matching TODO item li.input().click() break } } assertThat("one item checked", form.lis(By.className("done")).size(), equalTo(1)) println "Test Duration: " + (System.currentTimeMillis() - start) + " milliseconds." driver.close()
Лучшее время, прошедшее для теста, составило 326 миллисекунд. Конечно, быстрее, чем человек мог напечатать.
Тестирование приложения AngularJS с Selenium2
Angular готовится к выпуску 1.0 в настоящее время. Это примерно через 2,5 года! Поэтому я скопировал их текущий пример TODO на отдельный сайт GitHub-Pages . Вот как выглядит это приложение:
Вот DOM, как видно из Firebug. Снова поле ввода и циклическая конструкция показаны:
Angular использует более мелкозернистые атрибуты, чем Knockout. Опять же, атрибуты, которые только он понимает. ng-repeat, ng-model, ng-click, ng-show — это только некоторые из них. Это может немного облегчить их использование для локаторов Selenium2.
@Grapes([ @Grab("org.seleniumhq.selenium:selenium-java:2.20.0"), @Grab("org.seleniumhq.selenium.fluent:fluent-selenium:1.5.1"), @GrabExclude('xml-apis:xml-apis') ]) import org.openqa.selenium.By import org.openqa.selenium.firefox.FirefoxDriver import org.openqa.selenium.Keys import org.openqa.selenium.StaleElementReferenceException import org.seleniumhq.selenium.fluent.FluentWebDriverImpl import static org.hamcrest.CoreMatchers.equalTo; import static org.junit.Assert.assertThat; import static org.seleniumhq.selenium.fluent.FluentBy.attribute import static org.seleniumhq.selenium.fluent.Period.secs def driver = new FirefoxDriver() def fluent = new FluentWebDriverImpl(driver) driver.get "http://paul-hammant.github.com/angular_todo_app/" def todo = "abc 123 def 456" def start = System.currentTimeMillis() def form = fluent.div(attribute("ng-controller", "TodoCtrl")) // add an entry form.input(attribute("ng-model", "todoText")).sendKeys todo + Keys.RETURN //form.input(attribute("type", "submit")).click() assertThat("one item checked", form.spans(By.className("done-true")).size(), equalTo(1)) def lis = form.lis(attribute("ng-repeat", "todo in todos")) // loop through the todo items .. for (li in lis) { if (li.getText().contains(todo)) { // .. click the checkbox for the matching TODO item li.input().click() break } } assertThat("two items checked", form.spans(By.className("done-true")).size(), equalTo(2)) println "Test Duration: " + (System.currentTimeMillis() - start) + " milliseconds." driver.close()
Лучшее время для теста Angular / Groovy составило 330 миллисекунд, что более или менее совпадает с тестом Knockout. Еще быстрее, чем люди могут печатать. Также важно отметить, что команда Angular сменила пространство имен ‘ng’, обнаруженное в выпусках до 1.0, что означало преобладание двоеточий в HTML- источнике, на дизайн, в котором вместо этого используется тире. Что было нг: шоу теперь нг-шоу (и т. Д.). Это отличная новость для тестирования Selenium2, так как двоеточия были проблемой. Я ранее писал об этом.
Вывод
Angular и Knockout предоставляют Selenium2 удобные хуки для навигации. Хотя идентификаторы, управляемые фреймворком, были бы лучше, то, что там, достаточно хорошо. Используется более детализированные атрибуты, а не маленький язык, который Knockout имеет в одном атрибуте, и с которым будет легче ориентироваться. Тем не менее, удобные локаторы могут быть сделаны для:
// Angular form.input(ng.model("todoText")).sendKeys todo + Keys.RETURN def lis = form.lis(ng.repeat("todo in todos")) // Knockout form.input(ko.enterKey("add')")).sendKeys todo + Keys.RETURN def lis = form.ul(ko.foreach("todos")).lis()