Selenium Sandwich Part 1: Automating Selenium Sequences Steven Lembark Workhorse Computing lembark@wrkhors.com
Testing web front ends is different Much of the testing is manual. Tests validate multiple systems at once. Often cannot test without a running back end. Here is the first installment of a better way.
Been there? Hey where did that come from? Check the logs... Can't use that database: dropped it last week... Server isn't using your version any more...
Q: Why is it this bad? A: Because you aren't testing the front end.
Q: Why is it this bad? A: Because you aren't testing the front end. Q: Where do you get the content for testing?
Q: Why is it this bad? A: Because you aren't testing the front end. Q: Where do you get the content for testing? A: The back end.
Q: Why is it this bad? A: Because you aren't testing the front end. Q: Where do you get the content for testing? A: The back end. You are testing both ends at once.
Q: Why is it this bad? A: Because you aren't testing the front end. Q: Where do you get the content for testing? A: The back end. You are testing both ends at once. That makes front ends different.
Testing just the front end Isolation and repeatiblity are key to testing. Selenium helps with repeatability. Catch: Hardwired code required for each test.
Example Selenium::Driver code
Q: Is there a better way? A: Data driven code. With Object::Exercise code like: $object->method( @argz )->... Becomes [ $method => @argz ], ...
Daisy-chain logic find_element returns an object: $driver->find_element( ... )->click; $driver ->find_element( ... ) ->send_keys( ... ) ->send_keys( KEY->{ enter } );
False Lazyness Calls to “find_element” use the element. Hardwiring multiple calls is extra work. Why not “find_and_click”, “find_and_type”? A: Selenium interface doesn't declare them. Q: Why can't we do better than the interface?
True Lazyness Adding high-level abstractions is easy. Wrap the object. Add an AUTOLOAD. Redispatch low-level calls.
Selenium::Easy What would you use for a wrapper object?
Selenium::Easy What would you use for a wrapper object? Usual suggestion: { wrapped => $object }
Selenium::Easy What would you use for a wrapper object? Usual suggestion: { wrapped => $object } Downsides: Bulky, Slow, Klutzy->{ syntax }
Simpler wrapper sub construct { my $proto = shift; bless ( my $obj = '' ), blessed $proto || $proto }
Simpler packaging sub initialize { state $pkg = 'Remote::Selenium::Driver'; my $wrapper = shift; $$wrapper = $pkg->new( @_ ); $wrapper }
High-level methods sub find_and_key { my ( $wrapper, $find ) = splice @_,0,2; my $found = $$wrapper->find_element( @$find ); $found->send_key( $_ ) for @_, KEYS->{ enter }; } High-level methods
Dispatching low-level methods our $AUTOLOAD = ''; AUTOLOAD { my $wrapper = shift; my $i = 1 + rindex $AUTOLOAD, ':'; my $name = substr $AUTOLOAD, $i; $$wrapper->$name( @_ ) }
Q: Why not just drive a new class? A: Avoid knowing base class structure. AUTOLOAD and $$wrapper have no idea what the wrapped object is.
Replacing custom code Daisy chains now become single-steps: $wrapper->find_and_key( ... ); $wrapper->find_and_click( ... );
Replacing custom code Passed to Object::Exercise as: $wrapper->$exercise ( [ [ find_and_key => ... ], ... ], [ [ find_and_click => ... ], ... ], );
Replacing custom code Or with YAML like: --- - - - find_and_key - ... - - - find_and_click - ...
Data-driven equivalent # use Object::Exercise; # $wrapper->$exercise( $yaml ); --- - # verbose speicfic to "prepare_test" block. # prepare test ignores return values. - verbose - - prepare_test - - set_implicit_wait_timeout - 50000 - - get - http://www.google.com - - - find_send_keys - - q - name - YAPC Salt Lake City - - - find_click - '#rso li h3 a' - css
Data-driven equivalent - - regex - - get_title - YAPC - Title includes 'YAPC' - - - find_click - Talks and Schedule - - - find_click - Schedule - - - find_click - Wednesday - - - find_click - Selenium - partial_link_text - - regex - - get_title - Selenium - Title includes 'Selenium'
So ends our first episode... Developing wrapper classes in Perl is trivial. Repeatable test metadata is simple enough: Use high-level calls with Selenium. Coming up next: Isolating the front end.
=head1 SEE ALSO Good general talk on Selenium. Introduces the Selenium Playground on github: <http://www.slidesearchengine.com/slide/ testing-your-website-with-selenium-perl>
=head1 SEE ALSO Overview of Object::Exercise <http://www.slideshare.net/lembark/object- exercise?qid=6ed2ecf1-2520-4d4a-b6fa- 545b95693ebc&v=qf1&b=&from_search=1> Current documentation: <http://search.cpan.org/~lembark/Object- Exercise-3.02/lib/Object/Exercise.pm>

Selenium Sandwich Part 1: Data driven Selenium

  • 1.
    Selenium Sandwich Part1: Automating Selenium Sequences Steven Lembark Workhorse Computing lembark@wrkhors.com
  • 2.
    Testing web frontends is different Much of the testing is manual. Tests validate multiple systems at once. Often cannot test without a running back end. Here is the first installment of a better way.
  • 3.
    Been there? Hey wheredid that come from? Check the logs... Can't use that database: dropped it last week... Server isn't using your version any more...
  • 4.
    Q: Why isit this bad? A: Because you aren't testing the front end.
  • 5.
    Q: Why isit this bad? A: Because you aren't testing the front end. Q: Where do you get the content for testing?
  • 6.
    Q: Why isit this bad? A: Because you aren't testing the front end. Q: Where do you get the content for testing? A: The back end.
  • 7.
    Q: Why isit this bad? A: Because you aren't testing the front end. Q: Where do you get the content for testing? A: The back end. You are testing both ends at once.
  • 8.
    Q: Why isit this bad? A: Because you aren't testing the front end. Q: Where do you get the content for testing? A: The back end. You are testing both ends at once. That makes front ends different.
  • 9.
    Testing just thefront end Isolation and repeatiblity are key to testing. Selenium helps with repeatability. Catch: Hardwired code required for each test.
  • 10.
  • 11.
    Q: Is therea better way? A: Data driven code. With Object::Exercise code like: $object->method( @argz )->... Becomes [ $method => @argz ], ...
  • 12.
    Daisy-chain logic find_element returnsan object: $driver->find_element( ... )->click; $driver ->find_element( ... ) ->send_keys( ... ) ->send_keys( KEY->{ enter } );
  • 13.
    False Lazyness Calls to“find_element” use the element. Hardwiring multiple calls is extra work. Why not “find_and_click”, “find_and_type”? A: Selenium interface doesn't declare them. Q: Why can't we do better than the interface?
  • 14.
    True Lazyness Adding high-levelabstractions is easy. Wrap the object. Add an AUTOLOAD. Redispatch low-level calls.
  • 15.
    Selenium::Easy What would youuse for a wrapper object?
  • 16.
    Selenium::Easy What would youuse for a wrapper object? Usual suggestion: { wrapped => $object }
  • 17.
    Selenium::Easy What would youuse for a wrapper object? Usual suggestion: { wrapped => $object } Downsides: Bulky, Slow, Klutzy->{ syntax }
  • 18.
    Simpler wrapper sub construct { my$proto = shift; bless ( my $obj = '' ), blessed $proto || $proto }
  • 19.
    Simpler packaging sub initialize { state$pkg = 'Remote::Selenium::Driver'; my $wrapper = shift; $$wrapper = $pkg->new( @_ ); $wrapper }
  • 20.
    High-level methods sub find_and_key { my( $wrapper, $find ) = splice @_,0,2; my $found = $$wrapper->find_element( @$find ); $found->send_key( $_ ) for @_, KEYS->{ enter }; } High-level methods
  • 21.
    Dispatching low-level methods our$AUTOLOAD = ''; AUTOLOAD { my $wrapper = shift; my $i = 1 + rindex $AUTOLOAD, ':'; my $name = substr $AUTOLOAD, $i; $$wrapper->$name( @_ ) }
  • 22.
    Q: Why notjust drive a new class? A: Avoid knowing base class structure. AUTOLOAD and $$wrapper have no idea what the wrapped object is.
  • 23.
    Replacing custom code Daisychains now become single-steps: $wrapper->find_and_key( ... ); $wrapper->find_and_click( ... );
  • 24.
    Replacing custom code Passedto Object::Exercise as: $wrapper->$exercise ( [ [ find_and_key => ... ], ... ], [ [ find_and_click => ... ], ... ], );
  • 25.
    Replacing custom code Orwith YAML like: --- - - - find_and_key - ... - - - find_and_click - ...
  • 26.
    Data-driven equivalent # useObject::Exercise; # $wrapper->$exercise( $yaml ); --- - # verbose speicfic to "prepare_test" block. # prepare test ignores return values. - verbose - - prepare_test - - set_implicit_wait_timeout - 50000 - - get - http://www.google.com - - - find_send_keys - - q - name - YAPC Salt Lake City - - - find_click - '#rso li h3 a' - css
  • 27.
    Data-driven equivalent - - regex -- get_title - YAPC - Title includes 'YAPC' - - - find_click - Talks and Schedule - - - find_click - Schedule - - - find_click - Wednesday - - - find_click - Selenium - partial_link_text - - regex - - get_title - Selenium - Title includes 'Selenium'
  • 28.
    So ends ourfirst episode... Developing wrapper classes in Perl is trivial. Repeatable test metadata is simple enough: Use high-level calls with Selenium. Coming up next: Isolating the front end.
  • 29.
    =head1 SEE ALSO Goodgeneral talk on Selenium. Introduces the Selenium Playground on github: <http://www.slidesearchengine.com/slide/ testing-your-website-with-selenium-perl>
  • 30.
    =head1 SEE ALSO Overviewof Object::Exercise <http://www.slideshare.net/lembark/object- exercise?qid=6ed2ecf1-2520-4d4a-b6fa- 545b95693ebc&v=qf1&b=&from_search=1> Current documentation: <http://search.cpan.org/~lembark/Object- Exercise-3.02/lib/Object/Exercise.pm>