Thursday, February 21, 2008

FunFX and RSpec with stories

Hi again

I how now been playing around with RSpec and its story runner (rBehave). Its a fun way to write the tests as stories. This can work out nice with functional tests.

First thing you will need to do is to install RSpec (gem install rspec).

I have created the test towards the initial test application I put out, the one where you can add a product and a product item. A very simple application, but it shows how to work with FunFX in many ways.

When writing there are three main files (when writing for FunFX, addtional class files if you write for traditional Ruby classes) that must be written. And that is the story, a plain text file, the steps, the code behind the story (actual @ie.button("name").click actions) and a file to runn the stories, this file is often called all.rb and is running all tests you define in it.

When I searched the net for tips on how to organize the different files there were not created any best practice, as far as I could see. But when I asked Aslak Hellesøy a collegue and one of the creators of RSpec, he provided me with an example.

If you are to use some kind of ruby classes, they should go into a folder called lib, the step files should go into a folder called steps, and the story should go into a fodler called stories. The all.rb file and a Rake file is located at the root. This was not too spooky :-)

The first thing I did was to write the story. The story is writen as plain text, but in a certain way. There are certain words that are mapped to the steps file, and these are Given, When, And, and Then.

Below is the text that is in the story file. I think this is a nice way to write tests. This way the business side of a project may read the tests too.


Story: Add product

As an user
I want to be able to register and delete products
So that I can have control over the stock

Scenario: Add and delete product
Given the user is logged into the application
When the user click the Add product button
Then the product registration view is visible

Given the user enters Shirt into the product name textbox
And inputs Tennis into the product category textbox
And the user click the Add item button
And the user enters Pro into the item name textbox
And the user enters 9000 into the price textbox
And the user click the Ok button
Then the product registration view is closed

When the user selects Shirt from the datagrid
Then the screen shows the count of 1 item numbers

When the user drags the Shirt item from the datagrid to the trash
Then the datagrid has 0 number of products left

Then close the application


Since this was my first time implementing a rspec story I did not write entire story before I wrote the steps, but after you have written the story or in between as I did you will create the steps that will be taken at each line.

I have created the test towards the initial test application I put out, the one where you can add a product and a product item. A very simple application, but it shows how to work with FunFX in many ways.

Below is the steps file that implement the story file. One thing that you must remember is that the keyword And, is not used in the steps file. It just syas that the step is of the same type as the step above. And as I can see is only used for Given steps.


require 'funfx'

steps_for :product do
Given "the user is logged into the application" do
@ie = Funfx.instance
@ie.start(true)
@ie.speed = 1
@ie.goto("http://funfx.rubyforge.org/Flex/FlexRepeater/FlexRepeater.html", "FlexRepeater")
end

When "the user click the $button_name button" do |button_name|
@ie.button(button_name).click
end

Then "the product registration view is visible" do |balance|
@ie.title_window("Product registration").visible == "true"
end

Given "the user enters $text into the product name textbox" do |text|
@ie.text_area("tName").input(:text => text)
end

Given "inputs $text into the product category textbox" do |text|
@ie.text_area("tCategory").input(:text => text)
end

Given "the user click the $button_name button" do |button_name|
@ie.button(button_name).click
end

Given "the user enters $text into the item name textbox" do |text|
@ie.text_area("tfItemName").input(:text => text)
end

Given "the user enters $text into the price textbox" do |text|
@ie.text_area("tfPrice").input(:text => text)
end


Then "the product registration view is closed" do
@ie.title_window("Product registration") == nil
end

When "the user selects $text from the datagrid" do |text|
@ie.data_grid("dgOffer").select(:item_renderer => text)
end

Then "the screen shows the count of $item_number item numbers" do |item_number|
@ie.label("lNumberItems").text == item_number
end

When "the user drags the $item item from the datagrid to the trash" do |item|
@ie.data_grid("dgOffer").drag_start(:dragged_item => "Shirt")
@ie.box("deleteBox").drag_drop
end

Then "the datagrid has $number number of products left" do |number|
@ie.data_grid("dgOffer").num_rows == number
end

Then "close the application" do
@ie.unload
end
end


As Mark Anderson stated in the FunFX mailinglist, Rspec and stories does not provide a teardown method, the @ie.unload method must be played different. Michael Latta provided me with this solution. Add a step that might be called close the application, that will be added to all stories.

The all.rb file looks like this:


require 'rubygems'
require 'spec/story'
$:.unshift(File.dirname(__FILE__) + '/lib')
require 'steps/product'

with_steps_for :product do
run File.dirname(__FILE__) + '/stories/add_product'
end


Now it is just to run the all.rb file with ruby.

Tip: I have noticed that there are some problems with FunFX's ability to extract properties as text, automationName and so on with the Flex 3. Due to this one of the steps in the rspec example fail. But that is one more brilliant thing about rspec, it does not quit when it fails, it just states that the step failed.

3 comments:

Bindiya Mansharamani said...

Its a very informative post Peter. The way Mark and we came around with the unloading was by just checking if the @ie.nil. You know that the at_exit method is called at the time of tear down. So something like

at_exit do
if !$ie.nil?
@ie.unload
end

should work. I'm not sure if this is the right way but it is neat and works for us.

Bindiya Mansharamani

Peter Motzfeldt said...

Thank you. It seems like a nice way to do it, I will try to do it that way.

Ankur said...

i have a problem. i m using accordion but i m not able to select seconf thing from accordion. So i want proper command of accordion to access 2nd list...
Plz Help me