Saturday, March 2, 2013

Wrap Selenium Webdriver Calls for your benefit

Wrapping Webdriver API calls seems like a good idea to me. Not only do you get a level of abstraction that you can more closely control, but you can speak the language of your API because you are defining it. As an example lets examine the following code who's purpose is to wrap Selenium's selectFrame function call. In our case since we are using Selenium::Remote::Driver, switch_to_frame.

1:  sub select_frame {  
3:    my $self = shift;  
4:    my $element_type = shift;  
5:    my $name = shift;  
7:    if ($element_type) {  
9:      my $query = "SELECT element_name, locator  
10:             FROM html_element_tbl  
11:             WHERE element_type = '$element_type'  
12:             AND name = '$name'  
13:             AND is_active = true;";  
15:      # Get DB handler  
16:      my $dbh = Custom::Wepa::db_get_handle();  
18:      # Get Statement handler  
19:      my $sth = Custom::Wepa::db_get_st_handle($dbh, $query);  
21:      # Execute the statement  
22:      Custom::Wepa::db_execute($sth);  
24:      while (my($target, $locator) = $sth->fetchrow_array() ) {  
26:        $self->{driver}->switch_to_frame($target);  
27:      }      
28:    }  
29:    else {  
30:      $self->{driver}->switch_to_frame(undef)  
31:    }  
32:    return;  
34:  }  

In order to use this function you must first instantiate an object of the class this method is a member of; in this case that's (our main system interface module). Then you can call it with an HTML element type and element name. These are human readable names that have been "mapped" to their HTML counterparts. In our example they are: frame and payment_terms respectively, where frame and payment_terms are id tags of both these elements.

So what are the benefits being gained by the above wrap? One thing it is doing is adding a layer of abstraction to the way the UI is interacted with by introducing the concept of an object map where sometimes machine readable or cryptic names are mapped to human readable names. For example some QButton, might be mapped to Purchase Item, Button. This method seems to support two modes, which are the exact same modes the method it wraps supports:

  1. Switch to $target frame
  2. Switch to default frame (undef)
By wrapping calls like this and separating what us humans call the UI controls from what the application / computer calls it and creating a mapping between the two, we have effectively created a system that is more maintainable; but more importantly easier to maintain! AND your tests will never have to change just because the GUI controls changed ID's or were moved around.

How much easier to maintain? Consider that now that you have a catalog of elements and objects that your tests reference, this catalog can be automatically updated whenever a new checkin occurs that makes a change to an object already contained in the map table; effectively and automatically propagating the new information where it is consumed by the object map causing it to update itself with the new object information so the next time the tests run they wont fail just because of object mis-identification properties. As is normally the case when verifying GUI's.

No comments:

Post a Comment

Creative Commons License
VGP-Miami Web and Mobile Automation Blog by Alfred Vega is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.