Статьи

Тестирование нокаута и угловых с помощью Selenium2

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