DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
  1. DZone
  2. Coding
  3. Frameworks
  4. Testing Knockout and Angular with Selenium2

Testing Knockout and Angular with Selenium2

Paul Hammant user avatar by
Paul Hammant
·
Apr. 12, 12 · Interview
Like (0)
Save
Tweet
Share
5.19K Views

Join the DZone community and get the full member experience.

Join For Free

selenium2 ( aka —webdriver) is the de-facto standard web testing technology. it is good that selenium now garners more job postings than qtp , but that’s a different topic. the client-side mvc frameworks are a boon to web app development, but what about writing test scripts for the finished product? in this blog entry i will show how testable angularjs and knockoutjs applications are. in my opinion these two are the leading client-side mvc technologies, though there are alternatives like backbone.js that have significant market share. i prefer the two i’m highlighting over backbone for a range of reasons.

testing a knockoutjs app with selenium2

there’s a todo app on addy osmani’s technology comparison site . it uses knockout 2.0 (knockout 2.1 is in beta-testing presently). here’s what it looks like:

in testing the app, i’m going to try to write for an idiomatic knockout app. here’s the dom as firebug sees it:

i’ve highlighted ‘1’ – the input line where new entries are posted, and ‘2’ an attribute where knockout notes that it is looping through todo items. prevalent througout the dom are many more ‘data-bind’ attributes. these are evidence for knockout managing the page. html does not have that attribute, only knockout understands it. the browser just ignores the attributes it does not understand.

here is the selenium2 script (groovy) that tests this app. it is going to add and item, then mark it as ‘done’, then confirm that the ui displays it as done with a strike-through:

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

the best elapsed time for the test was 326 milliseconds. certainly faster than a human could type.

testing a angularjs app with selenium2

angular is gearing up for a 1.0 release presently. that’s about time after 2.5 years! as such, i’ve copied their current todo example to a stand-alone github-pages site . here is what that app looks like:

here’s the dom as seen in firebug. again the input field and looping construct are shown:

angular uses more fine-grained attributes than knockout. again, attributes that only it understands. ng-repeat, ng-model, ng-click, ng-show are just some of them. this might make it slightly easier to leverage them for selenium2 locators.

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

the best time for the angular/groovy test was 330 milliseconds which is more or less the same as the knockout one. still faster than humans can type. it is also important to note that the angular team have changed from ‘ng’ namespace found in the pre-1.0 releases that meant that colons were prevalent in the html soure, to a design where a dash is used instead. what was ng:show is now ng-show (etc). this is great news for selenium2 testing as the colons were a bit of a problem. i blogged previously on that.

conclusion

angular and knockout provide some handy hooks for selenium2 to navigate by. though ids managed by the framework would be better, what’s there is good enough. angular using more fine-grained attributes rather than the little language that knockout has in a single attribute and that is going to be easier to navigate with. that said convenience locators could be made for either:

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

Knockout (web framework) AngularJS app Attribute (computing)

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Automated Testing With Jasmine Framework and Selenium
  • Low-Code Development: The Future of Software Development
  • 5 Steps for Getting Started in Deep Learning
  • How To Create a Failover Client Using the Hazelcast Viridian Serverless

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: