wrap it with meaning def load_login_page ie.goto 'http://bigblabla.com/' end def enter_login_info ie.text_field(:name, 'ye_id').set 'BubbaJones74' ie.text_field(:name, 'ye_pass').set 'pigscanfly42' end def click_login ie.button(:value, 'open ye').click end #steps to execute load_login_page enter_login_info click_login
and put it in a test harness require 'test/spec' describe 'user logging to BlaBla awesome app' do it 'presents user with a page' do load_page; ie.title.should == 'BlaBla Login' end it 'lets user enter id and password' do ie.text_field(:name, 'ye_id').exists?.should.be true ie.text_field(:name, 'ye_pass').exists?.should.be true enter_login_info end it 'loads home page when you click login' do click_login; ie.title.should == 'BlaBla HomePage' end end
TaDa! Building a Trap
After 100 tests I realized I have built a trap.
I am now concerned with 4 things
- managing Browser, Pages and Elements
- sequence of events, scenarios
- and data sets.
- test execution, harness, runtime etc...
all of in the same place
Separate Concerns
Pages = Adapter for Watir. Domain Vocabulary for things of concern on the page.
UseCase = Sequence of Events, scenarios, actors, Domain Vocabulary
Scenario Data = Realization of UseCases
TestCases = use the existing frameworks for execution
Page Adapters
Declare interface as semantic intent mapping to Document Object Model implementation
class Page def initialize(browser) @browser = browser end def semantic_page_object browser.control(:what, how) end end page = Page.new(ie) page.semantic_page_object.action some_data
Let's try an example @author = {:first => 'Kurt', :last => 'Vonnegut', :dob => '11/11/1918', :books => ['Slaugherhouse 5', 'Hokus Pocus', 'Mother Night']} @page = SearchAuthorPage.new do first.set @author[:first] last.set @author[:last] dob.set @author[:dob] end @page.search # on a Author search page enter first name, last name and date of birth and click search.
Good Quotes Inspire
"The fundamental principle of Object Oriented programming is the unification of methods and data. Splitting this up inappropriately gets you right back to procedural programming."
Dave Thomas
http://pragprog.com/articles/tell-dont-ask
(there is a chance I misread it)
unify by convention
make hash keys match page objects and set values
@author = {:first => 'Kurt', :last => 'Vonnegut', :dob => '11/11/1918', :books => ['Slaugherhouse 5', 'Hokus Pocus', 'Mother Night']} @page = AuthorSearchPage.new @page.spray @author # set the page with data parameters @page.search # by convention maps hash keys to methods def spray(hashmap) hashmap.each_pair do |page_object, value| page_object.set value end end
Ducktape Watir
I needed RadioGroup, radios sharing the same name
# individual radios ie.radio(:name, 'ze_razio', 'value1') ie.radio(:name, 'ze_razio', 'value2') ie.radio(:name, 'ze_razio', 'value3') # as a radio_group rg = ie.radio_group(:name, 'ze_razio') rg.values # => ['value1, 'value2', 'value3'] rb.set 2 # => sets second radio in a group
Ducktape Watir
I needed CheckboxGroup, checkboxes that share the same name.
# individual checkboxes ie.checkbox(:name, 'ze_shekbochs', 'value1') ie.checkbox(:name, 'ze_shekbochs', 'value2') ie.checkbox(:name, 'ze_shekbochs', 'value3') # as a checkbox_group rg = ie.checkbox_group(:name, 'ze_shekbochs') rg.values # => ['value1, 'value2', 'value3'] rb.set 2 # => sets second checkbox in a group
Ducktape Watir
RadioGroup is like a Single SelectList
CheckboxGroup is like a Multi SelectList
unify Interface to Selectable Control
[SelectList,RadioGroup, CheckboxGroup].each do |control| # set control by item, value, position # query control about item(s), value(s), position(s) end
Moving from Page to Page
That's it with Page Objects
Now I need to model actor, her behaviour and value creation in context of a goal
I reach for UseCase object.
- provides sequence, recipe, scenario
and default data
UseCase Scenarios class UseCase attr_accessor :actor, :scenario, :page, :dataset include UseCaseSteps #decorate your usecase def initialize @dataset = some_data_call_to_populate_run @scenario = [:do_that, do_this] end def run @scenario.each {|task| self.send task} end end usecase = UseCase.new usecase.dataset.update :key => 'value', :bla => 'blabla' usecase.run
UseCases use UseCases
UseCase glues data to pages.
Acts like a Gluegun (act_as_gluegun)
UseCase is a wrapper for page interactions.
The intent is Actor executing a recipe to create value.
UseCase is a TestModel Abstraction
UseCase is a holder of needed Domain Objects holding values that have corresponding Page Object representations. (confused?)
UseCase example class MarekSchedulesDentistVisit < UseCase attr_accessor :request #my dataset @@scenario = [ :load_dentist_calendar, :find_date_and_time, :select_date_and_time, :enter_your_personal_info, :submit_request] end request={:date=>today,:name=>'marekj',:phone=>testdata[:phone]} usecase = MarekSchedulesDentistVisit.new usecase.request = request usecase.run
Stack of Abstractions
UseCases can be at different level of abstractions
class DoTripToTheMoon < UseCase
# has method :launch_rocket
class LaunchTheRocket < UseCase
# has method :fire_engines
UseCases calling UseCases def load_dentist_calendar usecase = LoadCalendar.new usecase.run end def find_date_and_time usecase = FindDateAndTime.new usecase.run end usecase = MarekSchedulesDentistVisit.new usecase.request = request usecase.load_dentist_calendar usecase.find_date_and_time # or usecase.run
Maybe Attach Test Framework
You can put UseCase execution in Test Framework
test/unit, rspec, test/spec, cucumber
require 'test/spec' describe 'requesting dentist appointment' do it 'does not allow to occur in the past' do @usecase = RequestDentistAppointment.new @usecase.request.update :date => yesterday @usecase.run # assert error should be here on submit @usecase.page.title.should == 'I can not has travel machine' end end
About Watirloo
Attempt to make Acceptance Testing written in the language of the Customer's domain propped by UseCase ideas and PageObjects Adapters
Extracted from real world watir frameworks I've implemented for clients: Health Insurance company and Airplane Parts management company.
Thank You Questions? Tomatos? http://github.com/marekj/watirloo
AWTA 2009 Presentation on Watirloo, a Watir Framewo more
AWTA 2009 Presentation on Watirloo, a Watir Framework. Page Adapters and UseCase scenario driven runners. Modeling Customer Language in Acceptance Tests less
0 comments
Post a comment