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()

