Thursday, July 9, 2009

What does taza need?

For those of you that haven’t taken a look at it I suggest you spend some time with taza. Taza is a ruby gem that provides an object oriented wrapper around web sites, pages, and flows. It provides an abstraction from the details of the pages as well as from the driver (selenium or watir) and target browser. The gem includes generators to create the shell of the desired classes. It was built for a specific purpose and serves that purpose well.

I first heard about taza from Joe O’Brien over breakfast one morning. He mentioned in passing that he was using it on a project. Since the gem was related to testing I had to take a look. At first it didn’t seem to fill a need I was having so I filed it away for later interrogation.

What do I need?
The company I work for (LeanDog) builds software and/or coaches companies hoping to learn how to become more agile or lean in there software development practices. I coach teams that develop software using J2EE and Ruby on Rails. It is not infrequent for my customers to have no test automation and often I am called upon to help setup the tools as well as train their developers and testers. As you might expect, I am very interested in automated testing.

I have standardized on using Cucumber for “acceptance testing” most web applications (both rails and J2EE) with JBehave being the exception on Java teams where they refuse to introduce Ruby. The tool works great for facilitating conversations between the customer and developers. Throughout numerous projects I have fine tuned my approach to using cucumber trying to maintain a balance between step granularity, good test design, and minimizing the need to refactor tests as the application grows and changes.

I have used both selenium and watir to drive the browser for our tests. Each gem has its’ strengths as well as weaknesses. We have learned over time that it is not a good idea to make calls to either API directly from our steps but rather to build up objects that represent our pages and encapsulate the details of the elements on the page (much like These objects are tedious to write and once written they are bound to a driver (either selenium or watir).

In order to make things easier for me and my teams I decided I would write a new gem that would help alleviate the tedium of writing the page objects as well as isolate them from the driver. As I was working out the details of this new project I remembered taza. Taza has much of what I need but is lacking in a few areas.

Rails Support
Currently taza generates a stand-alone project from which you create your page objects and tests. If you plan to test a rails application I believe it would be more appropriate to generate taza directly into the rails application. This would require the development of a new generator. This new generator would generate the following:

  • A site directory where the taza generated classes would reside

  • A taza.yml file in the config directory that would contain what is currently in the config.yml and .yml

These items would be generated directly into your rails application structure. Once taza is generated into your rails application it could be used by both cucumber and rspec tests. I would expect the classes that are generated inside of your rails application to be exactly the same as what is generated in a stand-alone project.

Element Generation
When you generate a page in taza you end up with a shell that looks like this:
module Google
class HomePage < Taza::Page


Next the developer must edit the generated class to add all of the elements from the html page. The google search page might look like this:
module Google
class HomePage < Taza::Page
element(:search_field) { browser.text_field(:name, 'q') }
element(:search_button) { borwser.button(:name, 'btnG') }

I believe there are two problems with this approach. The first is that the code written is directly coupled to the driver (I address this in more detail in the next section). The second problem is that all of the additional work a developer must perform setting up the page could be avoided if the generator were enhanced. I also believe this additional work is a roadblock for adoption. As a result the general approach today is to use something like webrat and couple the cucumber steps directly to elements on the page. Let me try to explain a different approach.

I would like to see the current taza page generator (and other generators within taza) enhanced to generate the page elements. Generating the above page would be very much like generating a rails model:
script/generate page search_field:text_field :search_button:button

The result of the above command would result in this class:
module Google
class HomePage < Taza::Page
text_field :search_field
button :search_button

For the google search the developer would then have to go in and set the name values like the code in bold below.
module Google
class HomePage < Taza::Page
text_field :search_field, :name => 'q'
button :search_button, :name => 'btnG'

Once the proceeding step was completed I would envision being able to use it in my test like this:
home_page.search_field = “Ruby” :search_button

Please note that I am not proposing the exact usage api here. This is currently just an idea and as such there are many issues to be fleshed out. I expect the fleshing to take place during development.

Imagine a typical rails application where we have a page representing a user model with name, address, city, state, and zipcode. The page could look like this:
module MyApp
class UserPage < Taza::Page
text_field :name
text_field :address
text_field :city
selection :state
text_field :zipcode

In our page class we could attempt to map the elements to items on the page with an id or name of “user_name”, “user_address”, etc. If we did not find user_name we could look for “name”. In many cases this would just work without any additional effort on the part of the developer. This follows the rails approach of convention over configuration.

Clearly this effort requires more than just changes to the generators. There would need to be significant changes made to the current taza library which I could see happening as part of the driver isolation effort.

Driver Isolation
Lets look at the google page object from the previous section:
module Google
class HomePage < Taza::Page
element(:search_field) { browser.text_field(:name, ‘q’) }
element(:search_button) { browser.button(:name, ‘btnG’) }

Later when this is used it is accessed like this:
home_page.search_field.set ‘ruby’

All of the code in bold is API from the driver - either selenium or watir. When you start building your objects with one driver you are stuck with it short of some major refactoring. It also keeps you from running your tests using both platforms if you so desire.

I believe taza should isolate the developer from the underlying driver much the same way that ActiveRecord provides isolation from different databases. This would allow developers to switch between drivers by changing the configuration yml files. A developer might want to do this if they determine that one driver works better with a specific browser while the other driver works better with yet another. There also might be cases where a driver adds support for a new browser type/version that is not available in the other.

The driver isolation would take the form of a thin veneer over both selenium and watir. The veneer would still allow developers to access the browser directly so they could use driver specific code where they deem necessary or on APIs that we do not yet provide in our wrapper. The goal of this effort would be to provide an API/DSL that would give developers the majority of the functionality they need to test web applications.

No comments:

Post a Comment