Efficient Rails Test-Driven Development - Week 6

37
Efficient Rails Test-Driven Efficient Rails Test-Driven Development — Week 6 Development — Week 6 Wolfram Arnold Wolfram Arnold www.rubyfocus.biz www.rubyfocus.biz In collaboration with: In collaboration with: Sarah Allen, BlazingCloud.net Sarah Allen, BlazingCloud.net marakana.com marakana.com

description

Learn how to apply the test-first approach to all of your Rails projects. In this six class series, experienced Rails engineer and consultant, Wolfram Arnold applies his real-world perspective to teaching you effective patterns for testing.In this sixth of six classes, Wolf discusses:- Integration frameworks (Cucumber, Webrat, Capybara, and Selenium)- Integration testing with Selenium (advantages and problems)- Page Objects- Locators (Selenium, CSS and XPath locators- RSpec Custom Matchers- Testing for Access Control** You can get the slides and source code from this presentation at: http://marakana.com/f/215 **Find more videos, tutorials, and code examples at http://marakana.com/techtv

Transcript of Efficient Rails Test-Driven Development - Week 6

Page 1: Efficient Rails Test-Driven Development - Week 6

Efficient Rails Test-Driven Efficient Rails Test-Driven Development — Week 6Development — Week 6

Wolfram ArnoldWolfram Arnoldwww.rubyfocus.bizwww.rubyfocus.biz

In collaboration with:In collaboration with:Sarah Allen, BlazingCloud.netSarah Allen, BlazingCloud.net

marakana.commarakana.com

Page 2: Efficient Rails Test-Driven Development - Week 6

Integration FrameworksIntegration Frameworks

Cucumber, Webrat, Capybara,...

lightweight browser without JS

fast

easy to set up

great for API testing

useful for exploratory application testing

Page 3: Efficient Rails Test-Driven Development - Week 6

Integration FrameworksIntegration Frameworks

Selenium

drives a browser (Firefox, IE, Safari, Chrome...)

slow

many moving pieces

many technology choices

great if client/server testing is a must

useful for exploratory application testing

Page 4: Efficient Rails Test-Driven Development - Week 6

SeleniumSelenium

Page 5: Efficient Rails Test-Driven Development - Week 6

Console ExperimentsConsole Experiments

irb

> require 'support/boot'

> Page.start_new_browser_session

> ...

> Homepage.visit

> Page.selenium_driver.try_something_here

> ...

> Page.close_current_browser_session

Page 6: Efficient Rails Test-Driven Development - Week 6

Procedural Selenium TestProcedural Selenium Test

page.open

page.type “username”, “joe”

page.type “password”, “secret password”

page.click “submit”, :wait_for => :page

page.text?(“My HomePage”).should be_true

page.click “link=New Messages”

...

Page 7: Efficient Rails Test-Driven Development - Week 6

Problems with thisProblems with this

● Very low level– hard to see what's being checked

– hard to reuse

– prone to copy & paste

● Next step– pull out common methods

– but common file quickly gets cluttered

– most tests only use a small fraction of common methods

Page 8: Efficient Rails Test-Driven Development - Week 6

Page ObjectsPage Objects

Page 9: Efficient Rails Test-Driven Development - Week 6

Page ObjectsPage Objects

Each Page represented by an Object

Methods are– things you can do

– things you can see

– apply to this page only

Each method returns a page object– itself

– another page object

Page 10: Efficient Rails Test-Driven Development - Week 6

AdvantagesAdvantages

● Reuse is easy● Documentation● Maintainability

– Changes to the UI or page require only one change of the test code, in only one place

● Method Chaining– Development of new tests is easy and fast

– Easy reuse of existing “library of things to do” on a page

Page 11: Efficient Rails Test-Driven Development - Week 6

How to build Page Objects?How to build Page Objects?

Model the UI in Page Objects

entire pages

parts of a page

Page 12: Efficient Rails Test-Driven Development - Week 6

LocatorsLocators

Page 13: Efficient Rails Test-Driven Development - Week 6

LocatorsLocators

Selenium Locators

“link=Sign in”

“username”

CSS Locators

“css=ul.global_nav_list li”

“css=input[name=username]”

XPath Locators

“xpath=//ul[@class='global_nav_list']/li”

“xpath=//input[@name='username']”

Page 14: Efficient Rails Test-Driven Development - Week 6

Selenium Locators—DefaultSelenium Locators—Default

“identifier” matches:

id attribute

element(“identifier=navigation”)

finds: <div id=”navigation”>

name attribute

element(“identifier=username”)

finds: <input name=”username”>

Note: matches id first, if not found, looks for name attribute

identifier keywordis optional

This is the defaultlocator

Page 15: Efficient Rails Test-Driven Development - Week 6

Other Selenium LoctorsOther Selenium Loctors

Explicit “id” locator:

matches id attribute

element(“id=navigation”)

finds: <div id=”navigation”>

Explicit “name” locator:

matches name attribute

element(“name=username”)

finds: <input name=”username”>

Page 16: Efficient Rails Test-Driven Development - Week 6

More on name locatorMore on name locator

The “name” locator may be followed by filters:

element(“name=usename value=Joe”)

Supported filters:

value=value pattern

index=element index, starting at 0

Page 17: Efficient Rails Test-Driven Development - Week 6

Link LocatorsLink Locators

The “link” locator is used for links:

element(“link=Sign in”)

matches the text (pattern) of an a tag:

<a href=”...”>Sign in</a>

Page 18: Efficient Rails Test-Driven Development - Week 6

Select from Drop-DownSelect from Drop-Down

select(selectLocator, optionLocator)

optionLocator matches on label by default

Example:

select(“name=language”,”French”)

Page 19: Efficient Rails Test-Driven Development - Week 6

Text VerificationText Verification

glob:pattern

Similar to filename matching no command line:* matches any sequence of characters

? matches any single character

regexp:pattern

match against a regular expression

regexpi:pattern

match against a regular expression, case-insensitive

exact:string

exact match

Page 20: Efficient Rails Test-Driven Development - Week 6

String matching examplesString matching examples

“Welcome John Smith”

glob:Welcome*

regexp:Welcome.*

regexpi:welcome.*

exact:Welcome John Smith

If nothing is specified, glob is the default.

Page 21: Efficient Rails Test-Driven Development - Week 6

CSS LocatorsCSS Locators

css=a

selects <a> tag

css=a.some_class

selects <a class=”some_class”>

css=a#some_id

selects <a id=”some_id”>

css=a.some_class#some_id

selects <a id=”some_id” class=”some_class”>

Page 22: Efficient Rails Test-Driven Development - Week 6

CSS Locators: AttributesCSS Locators: Attributes

css=input[name=username]

selects <input name=”username”>

css=img[src*=”btn_img”]

selects <img src=”http://example.com/btn_img?1234>

*= partial match

^= match beginning

$= match end

Page 23: Efficient Rails Test-Driven Development - Week 6

CSS Locators ChainedCSS Locators Chained

tag1 tag2

decendents

css=ul.navlist li a

<ul class=”navlist”><li>

<a>Some link</a></li>

<ul>

tag1+tag2

siblings

css=input+a

<input>

<a></a>

Page 24: Efficient Rails Test-Driven Development - Week 6

Waiting RulesWaiting Rules

Page 25: Efficient Rails Test-Driven Development - Week 6

When to Wait?When to Wait?

An Event Trigger

Click

Mouse over/out

Form Submit

Waiting for:

Page load

AJAX load

JavaScript action (e.g. hide/show)

Page 26: Efficient Rails Test-Driven Development - Week 6

Waiting for PageWaiting for Page

open “/”

will automatically wait for page

clicking on a link

wait_for :page

Page 27: Efficient Rails Test-Driven Development - Week 6

Wait for ElementWait for Element

Used for: Ajax, JavaScript

click “locator”,:wait_for => :element,:element => “locator”

Page 28: Efficient Rails Test-Driven Development - Week 6

Wait for VisibilityWait for Visibility

Something appearing or disappearing dynamically

click “locator”,:wait_for => :visible,:element => “locator”

Note: The element must be present (see wait for element) or visibility check will fail with a “no element found error”

Page 29: Efficient Rails Test-Driven Development - Week 6

RSpec Custom MatchersRSpec Custom Matchers

Page 30: Efficient Rails Test-Driven Development - Week 6

[1,2,3] == [2,3,1]

([1,2,3] – [2,3,1]).should be_empty

[1,2,3].should be_commutative_with([2,3,1])

http://wiki.github.com/dchelimsky/rspec/custom-matchers

http://railscasts.com/episodes/157-rspec-matchers-macros

Page 31: Efficient Rails Test-Driven Development - Week 6

Testing forTesting forAccess ControlAccess Control

Page 32: Efficient Rails Test-Driven Development - Week 6

Access ControlAccess Control

Controller access

Disallowed action should be blocked via before_filters

Most important!

View access

Disallowed pages/actions should not be linked to

Purely cosmetic

Page 33: Efficient Rails Test-Driven Development - Week 6

DeviseDevise

http://github.com/plataformatec/devise

Latest in Authorization Plugins

Comes with controllers and views

password recovery feature, etc.

Rack-based

Page 34: Efficient Rails Test-Driven Development - Week 6

Test Driven Test Driven DevelopmentDevelopment

Page 35: Efficient Rails Test-Driven Development - Week 6

Test-Driven DevelopmentTest-Driven Development

Benefit #1: Better Design

Testing as-you-go makes code better structured.

Testing emphasizes decoupling.

Fat model, skinny controller becomes easier.

Benefit #2: Focus & Project Management

If you don't know what to test for, how can you know what to code for?

Knowing when you're done.

Page 36: Efficient Rails Test-Driven Development - Week 6

Test-Driven DevelopmentTest-Driven Development

Benefit #3: Documentation & Collaboration

Tests are documentation that's always current.

Tests illustrate how code is meant to be used.

Benefit #4: Creation of Tests

Testing happens in-situ, not after the fact

When you're done coding, you've got a full test suite

No tedious chores and guilt afterwards

Page 37: Efficient Rails Test-Driven Development - Week 6

Inside-Out vs. Outside-InInside-Out vs. Outside-In

Start with what you understand

can be model, view or controller

When you discover you need something else...

make failing tests pending

implement what you're missing

resume

Discover the application inside out.