What do you do with bugs that you find in a sprint?

February 2, 2008

What do you do with bugs that you find in a sprint?

For those who are not familiar with an Agile software development processs, a sprint (or iteration) is an interval of time during which developers create the next set of deliverables.  A sprint is usually one to four weeks long.  At the end of the sprint, the deliverables are usually code that is ready for deployment.  In some cases for complex systems, it means that the code is production ready and can be tested (though it may not be ready for deployment because of other undeveloped pieces).

As we test during a sprint, the question often comes up, “Where do we put the bugs?”  That is, do we deal with them immediately, deal with them sometime during the sprint, or schedule them for a future sprint.  The answer, as usual, depends on several things.  If we find bugs in the functionality of the current sprint, then they are put on cards with “BUG” in big red letters and the card is placed in the column of work for the current sprint.  If possible, the testers develop automated tests to verify the bug is fixed and as  furture regression tests.  Any bugs that remain at the end of the sprint are entered into a bug tracking system so they will not be lost.  The card goes in the backlog for the next sprint and is prioritized by the customer with all the other cards in the backlog.

If the bug that is found is in legacy code, the bug is entered into a bug tracking system so that it will not be lost and it will be prioritized with other work for future sprints unless the functionality for the current sprint depends on getting this bug fixed.  In this case there will probably have to be an evaluation to see if the bug can be fixed in the sprint and the other work completed too.

Usually before there is a release there is a sprint that is devoted to bug fixing, practice deployments, and the actual deployment.  I have found it very useful to write the remaining bugs that must be fixed before the release on a whiteboard during one of these sprints.  All the bugs that have to be fixed before release and the top like-to-fix if there is time bugs, go on whiteboards.  In addition, I include tasks like practice deployments and release reviews too.  Any new bugs that are found during this sprint are added to the whiteboard.  A developer puts their initials next to one when they start working on it.  When they think they have fixed it, they write RTT by it (Ready To Test) along with the date and time.  If it passes testing, the tester writes “Done” next to it.  At the beginning of the next day, all the bugs that are “Done” are erased off the white board.  I believe this helps put all the work that needs to be done for the release out in front of everybody.  Instead of erasing the bugs that are fixed, you can simply draw a line through them if you want them to stay up for reference.  Personally, I like to erase them.  It gives more of a sense of progress as the white boards that were full of bugs turn more and more white.

I have used this at several different companies and it seems to work well when you are in a bug fix mode.  As with any Agile practice this should be adapted to your particular situation and culture.

Advertisement

What to do in a Watir test if you don’t have a unique id

January 21, 2008

OK, you don’t have ids on the controls you need to access.  Now what do you do.  I was faced with this situation.  I needed to verify that one menu item followed another.  Neither of the menu items had an id.  I could simply use indicies on the links.  That would have made for a very brittle test (one that breaks easily).  I happened to notice that the parent cell (TD) had a unique class (sideNav).  If I could associate my test with that cell, it would be a bit more stable.

Within the cell was a list structure (UL), with each list item (LI) followed by a link that contained text.  I was supposed to make sure that the link with text of “Item-of-Interest” was followed by the link with the text “Following-Item”.  The structure of this list was very regular: list item followed by a link, followed by text, followed by the next list item.
<TD class=sideNav>
    <UL>
       <LI>
         <A>
            #text
       <LI>
         <A>
            #text
       <LI>
         <A>
            #text
       <LI>
         <A>
            #text
       <LI>
         <A>
            #text
       <LI>
         <A>
            #text     <– Item-of-Interest
       <LI>
         <A>
            #text     <– Following-Item
       <LI>
         <A>
            #text
If I just used indicies to locate the links, an additional link to Item-of-Interest in front of the list could break the test.  That is not an unusual situation on our website.  In addition, using the “sideNav” class provided some additional insurance that I was testing what I thought I was testing.  I decided I could locate the cell with class=sideNav, loop through the list until I found the first text I was looking for and test the next link to see that it had the “Following-Item” text.  The code below is a sample of how to implement the test.

i = 1 
while $br.cell(:class, "sideNav").ul(:index,1).li(:index, i).link(:index, 1).exists? 
   if $br.cell(:class, "sideNav").ul(:index,1).li(:index, i).link(:index, 1).text =~ /Item-of-Interest/i 
      if $br.cell(:class, "sideNav").ul(:index,1).li(:index, i + 1).link(:index, 1).exists? 
         assert($br.cell(:class, "sideNav").ul(:index,1).li(:index, i + 1).link(:index, 1).text =~ /Following-Item/i) 
      end 
   end 
   i += 1 
end

The statement that begins with “while” and ends with “.exists?” is there to prevent the test from failing with an exception if the combination does not exist.  I loop through the list items with an index so that once I find the first text, I will look for the “Following-Item” text at index+1.  Again, I do an “exists?” test before I do the assert to keep the code from throwing an exception.

It turns out that this worked out OK for testing this condition.  Although it is not the best situation, using the cell class to identify the area was better than just using indicies.

Do yourself a favor (use id’s for Watir testing)

January 9, 2008

When you use Watir to test web pages, the best way to identify buttons and text fields is with ids.  The problem may be that the controls you want to test do not have ids.  The solution to this may be political rather than technical.  I know that developers and testers usually want to stay out of politics.  I have some ideas that may help you deal with the situation.

The first soultion involves finding a developer or developers who are willing to help you make it easier to test code.  There are some out there.  Usually they have worked in an environment where there is an emphasis on testing like Test Driven Development (TDD).  It could be that it is a forward thinking developer that realizes the advantages of having a good set of test suites for code they are developing.  If you can find developers who share this interest with you, it is the best way to procede.  Getting things done informally is usually the easiest and fastest way to getting things done.

If that does not work, the next thing to try is to sell your manager that it is to their benefit to get the developers to help you out.  Now I know you don’t like to “sell” anything.  The truth is that managers like things that save them money, make them look more productive, or make them look smarter to their managers.  Conviencing your manager to get the developers to add ids to the web pages will do this in the following three ways.

First, it will make the tests cheaper to develop.  You can change “cheaper” around to whatever your manager’s hot buttons are.  Basically it means that you can create more tests in the same amount of time or that you will not have to hire more testers. You will probably be able to start testing earlier because tests are easier to write.  You get the picture. You will have more tests earlier.

Second, tests will be less brittle.  If you are new to the testing world, a brittle test is one that breaks easily.  In web testing, this usually happens because you have to rely on tag structure based on position rather than an id that stays with a tag regardless of where it moves on the page.  If you end up testing while developers are still coding and you can use ids, your tests will not break as much.

Third, it will be cheaper to maintain your tests.  Undoubtedly there will be bug fixes or new features added to the original application.  Your tests will have to be modified. It will be easier for someone else to understand and maintain the tests if you use ids.
           
I really cannot stress enough how much time you will save and how much easier your life will be as a tester if you can get ids on the controls.  It would certainly be worth buying a developer a lunch or two if you can convence them to add ids for you.

If you look back at my blog post How to start writing web test scripts with Ruby and Watir, https://jimhmatthews.wordpress.com/2007/10/28/how-to-start-writing-web-test-scripts-with-ruby-and-watir/, you will notice that I had text_fields, ie.text_field(:id, “billing_name”) and a button, ie.button(:id, “checkout_button”) that were identified with a unique id.  Now if these move around on the page, my tests will not break because I can still find them with the id.
     def enter_billing_information
       assert(ie.text_field(:id, “billing_name).value == ”)
       assert(ie.text_field(:id, “billing_address”).value == ”)
       assert(ie.text_field(:id, “billing_city”).value == ”)
       assert(ie.text_field(:id, “billing_state”).value == ”)
       assert(ie.text_field(:id, “billing_zipcode”.value == ”)
       ie.text_field(:id, “billing_name).set’Joe Tester’
       ie.text_field(:id, “billing_address”).set’123 Address’
       ie.text_field(:id, “billing_city”).set’Austin”
       ie.text_field(:id, “billing_state”).set’TX’
       ie.text_field(:id, “billing_zipcode”).set’12345′
       assert(ie.text_field(:id, “billing_name).value == ‘Joe Tester’)
       assert(ie.text_field(:id, “billing_address”).value == ‘123 Address’)
       assert(ie.text_field(:id, “billing_city”).value == ‘Austin’)
       assert(ie.text_field(:id, “billing_state”).value == ‘TX’)
       assert(ie.text_field(:id, “billing_zipcode”).value == ‘12345’)
     end
and
     def checkout_button
       ie.button(:id, “checkout_button”)
     end
If you are going to the trouble to add unique ids, you might as well make them descriptive about what they reference.  Hence I chose “billing_name” and “checkout_button.”

Next time I will talk about what to do if you have to test a page that does not have ids and you cannot get them on there anytime soon if at all.

Negative Testing with Fit

December 13, 2007

Someone asked me how you do negative testing in Fit.  People generally refer to tests that are supposed to cause errors as negative tests.  The first thing is to get the positive test cases working, those that should not produce any errors.  If the positive cases are not passing, you cannot depend on any results.  Though some negative tests cases may be producing correct results, if positive test cases are not passing, you cannot depend on the results of negative test cases (those that should cause errors).  If the developers have to change code to get the positive tests working, they are likely to make a change that will affect the negative test cases.  All specified functions should have positive tests.  Get the positive test cases working before concentrating on getting the negative test cases working.

The developers in our group came up with the following simple fixture to deal with errors that generate error messages.  Note this fixture is designed around the idea that when there is an error, an error message is generated.  You include the fixture and put the following table in your test if you are not expecting an error message.  This is something you would put in your positive tests.

ErrorMessage
Messages

When you run your test, if there are no errors, you get the table with the two gray rows.  If on the other hand you get an error you are not expecting, it adds a new row to the table with the error message and the indication that this is surplus, i.e. something you were not expecting.  Since this is an error you are not expecting, it colors the row red.

ErrorMessage
Messages
Something went wrong. surplus

Now lets say that you were creating a negative test (one in which you were expecting an error) where you expected the error message “Something went wrong.”  You would create the following table and put it in your test.

ErrorMessage
Messages
Something went wrong.

When you run your test, if that is the only error you get, the row with the error message turns green.  You were expecting the error message and you got what you expected.

ErrorMessage
Messages
Something went wrong.

Now lets suppose that you ran the test where you were expecting the error message “Something went wrong,” and for whatever reason that error message was not produced.  In that case the fixture would indicate that the error message was missing and turned the row red.

ErrorMessage
Messages
Something went wrong. missing

Here is what happens if you are expecting the error message “Something went wrong,” got that message, but also got the additional error message “Something else went wrong.”  The message you were expecting would be shown in green and the additional message would be shown in red as surplus.

ErrorMessage
Messages
Something went wrong.
Something else went wrong. surplus

When we ask someone about the second error message, we learn that it was due to a change in requirements (of course this would never happen in the real world) and that it really should be there.  Now we can add that row to our fixture as an expected error message as shown below.

ErrorMessage
Messages
Something went wrong.
Something else went wrong.

The next time we run the tests, we are expecting both error messages and getting both error messages so everybody is happy.

ErrorMessage
Messages
Something went wrong.
Something else went wrong.

The book Fit for Developing Software discusses error reporting in a ColumnFixture and in an ActionFixture.  We built a number of DoFixtures to execute actions in our application.  When the action caused an error, the Error Message fixture displayed the error message.  We found this method very effective for testing error handling in the application. 

Get Fit ( but not necessarily FitNesse )

December 4, 2007

As I said in “About Me”, I believe in using whatever tool makes sense for what you are trying to test.  In a recent situation, we needed to test a “rules engine” for modifying URLs.  The test cases were pretty straight forward.   They were the current URLs and what we expected them to be after being modified by the rules engine.

The test tool we chose for this situation is “Fit”.  You may have heard of Fit and FitNesse before.  FitNess is the wiki that was originally used to drive Fit. For background information on Fit and FitNesse, go to http://fit.c2.com/

There are a number of drawbacks of using FitNesse to drive the tests.  The two most significant ones are that it is difficult to do version control of the test cases and that only one set of tests can be run from the wicki at a time.

The solution was to run the tests with Fit but not use FitNesse.  This solved the two main problems of using FitNesse.  First, the tests could be put under version control.  Second, anyone can checkout the tests and the Fit driver to their system and run the tests without worrying if someone else is running the tests.

We chose Fit because the test cases are pairs of inputs and expected outputs.  Fit allows you to specify a table with a column of inputs and a column of expected outputs.  Each row is a test case with input and the expected output.  We knew from the beginning that we were going to have a large number of test cases.  Using a table structure made it easy to manage data for the tests.  The table below shows an example of what inputs and expected results might look like.

inputurl1 expectedoutputurl1
inputurl2 expectedoutputurl2
inputurl3 expectedoutputurl3

Anyone who is familiar with the application should be able to understand the inputs and expected results.  Developers as well as testers are able to create test case pairs.  Developers can run the tests on their system while they are writing new code.

The test cases we developed fell into four categories.  The first was the existing URLs that we expected to be modified in a certain way.  The second was the existing URLs that we expected to be unmodified by the rules engine.  The third set of cases were ones where there were currently not any existing URLs but test URLs that should be modified in a certain way because of the rules.  The fourth set of cases was currently non-existent URLS that should not be modified by the rules engine.

We have implemented this testing system for the rules engine and it has proven effective.  There are about 300 test case pairs.  The test cases were developed by two testers and three developers.

For a further discussion of where Fit can be used as an effective testing tool, see the following link:

http://awta.wikispaces.com/Fit+-+Fitness+Implementation+Strategy

How to start writing web test scripts with Ruby and Watir

October 28, 2007

This blog entry is about how to start writing web test scripts with Ruby and Watir.  Watir is basically a library that lets you interface Ruby to Microsoft Internet Explorer (IE).  I originally presented this as a lightning talk at the Austin Alt.Net conference and one of my colleagues, Jason Darling http://blogs.dovetailsoftware.com/blogs/jason_darling/default.aspx, suggested that I blog it.  I am assuming you know something about Ruby and Watir or you would not be reading this.  If you don’t, I have included some links at the bottom of the post to get you started.  This is going to be rather lengthy so please bear with me.

We will start with a fairly simple user story in the paragraph below.

A user can add an item to the shopping cart.  The user can then proceed to checkout, add shipping information, billing information, and complete the transaction with a valid credit card.  When the transaction is complete, the user will be presented with the item(s) ordered, the total price of the order, the shipping information entered, and the billing information entered.

I tell the people I train to just write out the test steps like they would normally do.  For example, they may do something like this:

    Add an item to the shopping cart

Now comes the first rule:

1. All the words have to be lower case.

 Our first line becomes:

     add an item to the shopping cart

That brings us to rule number two:

2.  Put underlines between words instead of spaces.

  Our first line becomes:

     add_an_item_to_the_shopping_cart

I also like to name the method in the form of “verb” “object”, add_item, verify_information, etc.  We will now use these rules on the rest of the test steps:

The user can then proceed to checkout — so we add:
   “click_go_to_checkout_button”

add shipping information, —
  “enter_shipping_information”

billing information, —
  “enter_billing_information”

complete the transaction with a valid credit card —
  “enter_valid_credit_card_information”
  “click_checkout_button”

the user will be presented with the item(s) ordered —
  “verify_item_ordered”

the total price of the order —
  “verify_total_amount”

shipping information entered —
  “verify_shipping_information”

billing information entered —
  “verify_billing_information”

Putting it all together,our test scenario looks like what follows.  I made some assumptions based on my hypothetical model – like buttons that had to be clicked.
     add_an_item_to_the_shopping_cart
     click_go_to_checkout_button
     enter_shipping_information
     enter_billing_information
     enter_valid_credit_card_information
     click_checkout_button
     verify_item_ordered
     verify_total_amount
     verify_shipping_information
     verify_billing_information

OK, that’s it.  That’s the test scenario.  Anyone who is familiar with what the company does, should be able to read this and have a basic understanding of what is going on.  This scenario should be saved in a file with a .rb extension, which we will come back to later.  Give the file a descriptive name like book_purchase_with_credit_card.rb At this point we have outlined “what” needs to happen and now we need to define the “how”.

I recommend doing the following in a separate file.  Open the new file to do the following steps.  We will turn each of the “whats” into “hows” by turning each of the “whats” into a method.  A method implements the things that need to happen for that step.  We define the method by adding a “def” in front of the line and ending it with an “end” statement so it looks like:

     def add_an_item_to_the_shopping_cart

     end

Let’s say we want to order the book “Everyday Scripting with Ruby” by Brian Marick, which by the way is an excellent book for beginners.  In our application you select the book by selecting the title from a dropdown list.  To do that you need to do the following:

     ie.select_list(:id, “selectbook”).select’Everyday Scripting with Ruby’

“Wooowwwhhh,” you say, “How did you know that?”  “I’m glad you asked.”  There is a lot to get through in this single statement.  First, remember I said that Watir works with IE.  That is why the first thing is “ie”.  It does not have to be that way but trust me it is less confusing for now.  The “select_list” is the way in Watir you specify that you want to access a dropdown select list.  Yes, but there are several select_lists on the screen, how do you specify which one you want to access?  The “(:id, “selectbook”)” is the way you identify the select_list on the page that you want to act on.  To finish this off, “.select” is a method on select_list that allows you pick which item in the list you want to select.  ‘Everyday Scripting with Ruby’ is the exact text of the item in the list that you want to select.

Watir referrs to text boxes where you type in text, like where you would enter your name, as a “text_field,” a check box as a “check_box,” a radio button as a “radio,” a regular button as a “button,” a drop-down select list as a “select_list,” etc.  You are going to have to look through the Watir documentation for the Watir “names” for different controls.  A confusing thing about some of these is that they are often just referred to as “input” in the html.  The easiest thing to do is use your eyes to figure out which “input” they are.

The next part is a little harder.  It is how to uniquely identify the control on the page.  Watir supports a number of different ways to identify the controls and again I refer you to the Watir documentation.  You can use “id”, “name”, “class”, “url”, and a number of others depending on the type of the control and what is actually defined for the control.  For example, a text_field that is used to input a name may have an “id” of “name,” text_field(:id, “name”).   Sometimes you have several choices as to what to use.  If a control has a unique “id”, then I suggest using that.  To find out what is defined for a particular control, it is best to use something like the Microsoft IE Developer Toolbar. 

At the time of writing, you can get it at:

http://www.microsoft.com/downloads/details.aspx?familyid=e59c3964-672d-4511-bb3e-2d5e1db91038&displaylang=en

The IE toolbar will allow you to see controls and what attributes they have.  If you are lucky, you will have  unique ids for each of the controls you have to deal with.  If you don’t, ask your developers to add them for the ones you need.  If that does not work, there are different ways to identify them but that is a separate blog or look in more detail at the Watir documentation.

Back to the task at hand.  Our first method looks like this:

     def add_an_item_to_the_shopping_cart
       ie.select_list(:id, “selectbook”).select’Everyday Scripting with Ruby’
     end

That should be all we need for this method.

Let’s do the next method, click_go_to_checkout_button.

     def click_go_to_checkout_button
       ie.button(:id, “goto_checkout_button”).click
     end

The “ie.button(:id, “goto_checkout_button”)” identifies the button on the page and the “.click” is the method to click a button.

The next method is – enter_shipping_information.  I am going to use simplified information for shipping; a single name field, a single address field, a city field, a state field, and a zip code field.

     def enter_shipping_information
       ie.text_field(:id, “shipping_name).set’Joe Tester’
       ie.text_field(:id, “shipping_address”).set’123 Address’
       ie.text_field(:id, “shipping_city”).set’Austin”
       ie.text_field(:id, “shipping_state”).set’TX’
       ie.text_field(:id, “shipping_zipcode”).set’12345′
     end

That is all we would really need, except that I like to add a few more things to blanket information like this.  First, I like to make sure there was nothing left over in the fields from a previous run.  To do that, I do and assert ” (two single quotes) on each of the fields.  The assert expects to have “true” returned.  If is does not, it throws an exception.  So, asserting that the value in a field is  ”  means that if the field is empty, the result is “true” and the assertion passes.  If the field is not empty, then the expression evaluates to “false” and an exception is thrown.  After I have set the values, I like to make sure they contain the value I set.  To do that I do an assert of ‘<value that I set>’.  The method becomes:

     def enter_shipping_information
       assert(ie.text_field(:id, “shipping_name).value == ”)
       assert(ie.text_field(:id, “shipping_address”).value == ”)
       assert(ie.text_field(:id, “shipping_city”).value == ”)
       assert(ie.text_field(:id, “shipping_state”).value == ”)
       assert(ie.text_field(:id, “shipping_zipcode”.value == ”)
       ie.text_field(:id, “shipping_name).set’Joe Tester’
       ie.text_field(:id, “shipping_address”).set’123 Address’
       ie.text_field(:id, “shipping_city”).set’Austin”
       ie.text_field(:id, “shipping_state”).set’TX’
       ie.text_field(:id, “shipping_zipcode”).set’12345′
       assert(ie.text_field(:id, “shipping_name).value == ‘Joe Tester’)
       assert(ie.text_field(:id, “shipping_address”).value == ‘123 Address’)
       assert(ie.text_field(:id, “shipping_city”).value == ‘Austin’)
       assert(ie.text_field(:id, “shipping_state”).value == ‘TX’)
       assert(ie.text_field(:id, “shipping_zipcode”).value == ‘12345’)
     end

The billing method is similar:

     def enter_billing_information
       assert(ie.text_field(:id, “billing_name).value == ”)
       assert(ie.text_field(:id, “billing_address”).value == ”)
       assert(ie.text_field(:id, “billing_city”).value == ”)
       assert(ie.text_field(:id, “billing_state”).value == ”)
       assert(ie.text_field(:id, “billing_zipcode”.value == ”)
       ie.text_field(:id, “billing_name).set’Joe Tester’
       ie.text_field(:id, “billing_address”).set’123 Address’
       ie.text_field(:id, “billing_city”).set’Austin”
       ie.text_field(:id, “billing_state”).set’TX’
       ie.text_field(:id, “billing_zipcode”).set’12345′
       assert(ie.text_field(:id, “billing_name).value == ‘Joe Tester’)
       assert(ie.text_field(:id, “billing_address”).value == ‘123 Address’)
       assert(ie.text_field(:id, “billing_city”).value == ‘Austin’)
       assert(ie.text_field(:id, “billing_state”).value == ‘TX’)
       assert(ie.text_field(:id, “billing_zipcode”).value == ‘12345’)
     end

To pay the bill, we have to enter a valid (test) credit card.

     def enter_valid_credit_card_information
       ie.select_list(:id, “select_credit_card_type”).select’MC’
       assert(ie.text_field(:id, “credit_card_number).value == ”)
       ie.text_field(:id, “credit_card_number).set ‘1234567890123456’
       assert(ie.text_field(:id, “credit_card_number).value == ‘1234567890123456’)
       ie.select_list(:id, “credit_card_expiration_month”).select’August’
       ie.select_list(:id, “credit_card_expiration_year”).select’2008′
     end

Complete the checkout:

     def click_checkout_button
       ie.button(:id, “checkout_button”).click
     end

There is often a summary of the order at then end.  It contains what was ordered, total cost, shipping information, and billing information.  I normally look at each of these separately.  I have made some assumptions of additional total fields on the page.

     def verify_item_ordered
       assert(ie.text_field(:id, “item_ordered”).value == ‘Everyday Scripting with Ruby’
     end

     def verify_total_amount
       item_amount = ie.text_field(:id, “item_amount).text
       shipping_amount = ie.text_field(:id, “shipping_amount”).text
       total_amount = ie.text_field(:id, “total_amount).text
       assert(add_it_up(item_amount, shipping_amount, total_amount))
     end

I need to explain about the method “add_it_up(item_amount, shipping_amount, total_amount).  I am assuming that it is a method defined someplace else that takes the text amounts, converts them into numbers, adds them together, and returns “true” if item_amount and shipping_amount add up to total_amount.  It is not hard to do but I am not including it for simplicity.  I included the reference because I did not want to give you the false impression that you could simply add text numbers together.

     def verify_shipping_information
       assert(ie.text_field(:id, “review_shipping_name).value == ‘Joe Tester’)
       assert(ie.text_field(:id, “review_shipping_address”).value == ‘123 Address’)
       assert(ie.text_field(:id, “review_shipping_city”).value == ‘Austin’)
       assert(ie.text_field(:id, “review_shipping_state”).value == ‘TX’)
       assert(ie.text_field(:id, “review_shipping_zipcode”).value == ‘12345’)
     end

     def verify_billing_information
       assert(ie.text_field(:id, “review_billing_name).value == ‘Joe Tester’)
       assert(ie.text_field(:id, “review_billing_address”).value == ‘123 Address’)
       assert(ie.text_field(:id, “review_billing_city”).value == ‘Austin’)
       assert(ie.text_field(:id, “review_billing_state”).value == ‘TX’)
       assert(ie.text_field(:id, “review_billing_zipcode”).value == ‘12345’)
     end

Save the file with all these method definitions in a file called something like book_purchase_with_credit_card_defs.rb in the same directory as the book_purchase_with_credit_card.rb.  I like to use a name that is descriptive about the particular application I am testing.

We have basically all the pieces we need to run the tests.  Now we have to assemble them into a test that runs.  We have to add some things to the test file, book_purchase_with_credit_card.rb, to define the test case and make it work.  The finished file would look something like this:

dir = File.dirname(__FILE__)

require “#{dir}\/book_purchase_with_credit_card_defs”

require ‘watir’
require ‘watir/testcase’

$ie = Watir::IE.new_process
$ie.set_fast_speed

class TC_BookCheckoutWithCreditCard < Watir::TestCase

  include BookPurchaseWithCreditCard

  def test_01_book_purchase_with_credit_card
    $ie.goto”http://www.mywebsite.com
    add_an_item_to_the_shopping_cart
    click_go_to_checkout_button
    enter_shipping_information
    enter_billing_information
    enter_valid_credit_card_information
    click_checkout_button
    verify_item_ordered
    verify_total_amount
    verify_shipping_information
    verify_billing_information
  end
end

That should do it for the test case file.  I will offer some simple explanations for the things I added to this file, but you will have to read some Ruby reference material to better understand it.

dir = File.dirname(__FILE__) – Assigns the directory path of the current file to the variable “dir”

require “#{dir}\/book_purchase_with_credit_card_defs” – will read in the contents of the file (book_purchase_with_credit_card_defs) from the current directory.  (Remember, I said to save it to the same directory)

require ‘watir’ – causes all the Watir definitions to be read in

require ‘watir/testcase’ – causes all the Watir test definitions to be read in

$ie = Watir::IE.new_process – says to create a new IE object and assign it to the global variable $ie.  I know that a lot of programmers argue against using global variables, but this is one case where I think it is justified.

$ie.set_fast_speed  – this causes the script to execute quickly.  You may want to comment this out if you are having problems so you can see what is happening

class TC_BookCheckoutWithCreditCard < Watir::TestCase  – defines a new test class which inherits from the Watir TestCase class.  The test class name is made up of words that are run together with their first letter capitalized.

include BookPurchaseWithCreditCard  – This keeps you from having to explicitly reference the module with methods in your defs file.  This is a simpler way to do things for now.  You can research the topic for further information.

def test_01_book_purchase_with_credit_card – This line actually defines a method that is your test case.  It should start with “test_” to allow several test runner programs to find it.  I also number them in the order I want them to run (test_01_).  Some test runner programs run the tests in alphabetical order so this is the simplest way to ensure that your tests are run in the order that you want.

$ie.goto”http://www.mywebsite.com” – I added this to show how you can go to the web site you want to test.

The only thing else new is the two “end” statements.  The first one is for the test_01_book_purchase_with_credit_carddefinition and the second is for the test case class definition, class TC_BookCheckoutWithCreditCard < Watir::TestCase.

That finishes the test case file.  There are a few things we have to do to finish up the “defs” file.  The final “defs” file should look like this:
require ‘watir’
require ‘watir/testcase’

module BookPurchaseWithCreditCard

  include Test::Unit::Assertions

     def add_an_item_to_the_shopping_cart
       $ie.select_list(:id, “selectbook”).select’Everyday Scriptiong in Ruby’
     end

     def click_go_to_checkout_button
       $ie.button(:id, “goto_checkout_button”).click
     end

     def enter_shipping_information
       assert($ie.text_field(:id, “shipping_name).value == ”)
       assert($ie.text_field(:id, “shipping_address”).value == ”)
       assert($ie.text_field(:id, “shipping_city”).value == ”)
       assert($ie.text_field(:id, “shipping_state”).value == ”)
       assert($ie.text_field(:id, “shipping_zipcode”.value == ”)
       $ie.text_field(:id, “shipping_name).set’Joe Tester’
       $ie.text_field(:id, “shipping_address”).set’123 Address’
       $ie.text_field(:id, “shipping_city”).set’Austin”
       $ie.text_field(:id, “shipping_state”).set’TX’
       $ie.text_field(:id, “shipping_zipcode”).set’12345′
       assert($ie.text_field(:id, “shipping_name).value == ‘Joe Tester’)
       assert($ie.text_field(:id, “shipping_address”).value == ‘123 Address’)
       assert($ie.text_field(:id, “shipping_city”).value == ‘Austin’)
       assert($ie.text_field(:id, “shipping_state”).value == ‘TX’)
       assert($ie.text_field(:id, “shipping_zipcode”).value == ‘12345’)
     end

     def enter_billing_information
       assert($ie.text_field(:id, “billing_name).value == ”)
       assert($ie.text_field(:id, “billing_address”).value == ”)
       assert($ie.text_field(:id, “billing_city”).value == ”)
       assert($ie.text_field(:id, “billing_state”).value == ”)
       assert($ie.text_field(:id, “billing_zipcode”.value == ”)
       $ie.text_field(:id, “billing_name).set’Joe Tester’
       $ie.text_field(:id, “billing_address”).set’123 Address’
       $ie.text_field(:id, “billing_city”).set’Austin”
       $ie.text_field(:id, “billing_state”).set’TX’
       $ie.text_field(:id, “billing_zipcode”).set’12345′
       assert($ie.text_field(:id, “billing_name).value == ‘Joe Tester’)
       assert($ie.text_field(:id, “billing_address”).value == ‘123 Address’)
       assert($ie.text_field(:id, “billing_city”).value == ‘Austin’)
       assert($ie.text_field(:id, “billing_state”).value == ‘TX’)
       assert($ie.text_field(:id, “billing_zipcode”).value == ‘12345’)
     end

     def enter_valid_credit_card_information
       $ie.select_list(:id, “select_credit_card_type”).select’MC’
       assert($ie.text_field(:id, “credit_card_number).value == ”)
       $ie.text_field(:id, “credit_card_number).set’1234567890123456′
       assert($ie.text_field(:id, “credit_card_number).value == ‘1234567890123456’)
       $ie.select_list(:id, “credit_card_expiration_month”).select’August’
       $ie.select_list(:id, “credit_card_expiration_year”).select’2008′
     end

     def click_checkout_button
       $ie.button(:id, “checkout_button”).click
     end

     def verify_item_ordered
       assert($ie.text_field(:id, “item_ordered”).value == ‘Everyday Scripting with Ruby’
     end

     def verify_total_amount
       item_amount = $ie.text_field(:id, “item_amount).text
       shipping_amount = $ie.text_field(:id, “shipping_amount”).text
       total_amount = $ie.text_field(:id, “total_amount).text
       assert(add_it_up(item_amount, shipping_amount, total_amount))
     end

     def verify_shipping_information
       assert($ie.text_field(:id, “review_shipping_name).value == ‘Joe Tester’)
       assert($ie.text_field(:id, “review_shipping_address”).value == ‘123 Address’)
       assert($ie.text_field(:id, “review_shipping_city”).value == ‘Austin’)
       assert($ie.text_field(:id, “review_shipping_state”).value == ‘TX’)
       assert($ie.text_field(:id, “review_shipping_zipcode”).value == ‘12345’)
     end

     def verify_billing_information
       assert($ie.text_field(:id, “review_billing_name).value == ‘Joe Tester’)
       assert($ie.text_field(:id, “review_billing_address”).value == ‘123 Address’)
       assert($ie.text_field(:id, “review_billing_city”).value == ‘Austin’)
       assert($ie.text_field(:id, “review_billing_state”).value == ‘TX’)
       assert($ie.text_field(:id, “review_billing_zipcode”).value == ‘12345’)
     end
  end

We are almost done.  Let me give some simple explanations about the additional lines in this file.
require ‘watir’
require ‘watir/testcase’

I have included these again to avoid any possible problems.  You may want to experiment and see which ones you really need in each of the files.

module BookPurchaseWithCreditCard – Defines the module that contains the method definitions you are using.  Note the ”  include BookPurchaseWithCreditCard” line in your test file has to match this module name.

include Test::Unit::Assertions – Allows you to use the “assert” statements in this file.

Note that I have changed all the “ie” (which is a local variable) to “$ie” (which is a global variable).

The last thing is the “end” statement which is the “end” for the module definition.

That is it.  This will not actually run since it is not designed for a specific web site.  If you implemented the “add_it_up” method, you could put it into this “defs” file too.  There is some useful refactoring you can do on this once you get things working.  Get it working first and then refactor.  Some simple things you can do would be to change the following methods from:

     def click_go_to_checkout_button
       $ie.button(:id, “goto_checkout_button”).click
     end

and

     def click_checkout_button
       $ie.button(:id, “checkout_button”).click
     end

to

     def go_to_checkout_button
       $ie.button(:id, “goto_checkout_button”)
     end

and

     def checkout_button
       $ie.button(:id, “checkout_button”)
     end

and then change your test from

    click_go_to_checkout_button
and
    click_checkout_button

to

    go_to_checkout_button.click
and
    checkout_button.click

This puts the “.click” in your test case which is not a big deal, but it allows you to use other button methods on the buttons from your test case.

A more useful thing may be to refactor the “enter_valid_credit_card_information” method to generalize it for other credit cards.  That way you could use it and pass in all the credit card information.  So, you might change it from:
     def enter_valid_credit_card_information
       $ie.select_list(:id, “select_credit_card_type”).select’MC’
       assert($ie.text_field(:id, “credit_card_number).value == ”)
       $ie.text_field(:id, “credit_card_number).set’1234567890123456′
       assert($ie.text_field(:id, “credit_card_number).value == ‘1234567890123456’)
       $ie.select_list(:id, “credit_card_expiration_month”).select’August’
       $ie.select_list(:id, “credit_card_expiration_year”).select’2008′
     end

to something like this:
     def enter_credit_card(type, number, expire_month, expire_year)
       $ie.select_list(:id, “select_credit_card_type”).select”#{type}”
       assert($ie.text_field(:id, “credit_card_number).value == ”)
       $ie.text_field(:id, “credit_card_number).set”#{number}”
       assert($ie.text_field(:id, “credit_card_number).value == “#{number}”)
       $ie.select_list(:id, “credit_card_expiration_month”).select”#{expire_month}”
       $ie.select_list(:id, “credit_card_expiration_year”).select”#{expire_year}”
     end

and then change your test case to:

     enter_credit_card(“MC”, “1234567890123456”, “August”, “2008”)

The variables in enter_credit_card are local variables; type, number, expire_month, expire_year.  They are originally defined in the method definition statement:

enter_credit_card(type, number, expire_month, expire_year)

When you want to use the string value of the variable, the construct “#{type}” says to substitute the string value stored in the variable “type”.

That should get you started.  There are several things I said to do to keep it simple.  If you want to know the detail, you should read Watir or Ruby reference material.  I will end with a list of most common beginner problems:

1.  Capitalizing Watir in the require statement:  require ‘watir’  NOT  require ‘Watir’.
2.  Not putting an “end” for the class statement in the test case file.
3.  Leaving out the “class” statement in the test case file.
4.  Not specifying that your test class inherits from Watir::TestCase.
5.  Not starting the test case definition with def “test_”
6.  If you are using a “defs” file like I showed  you, not including the exact name of the “defs” module in the test case file (within the class definition).
7.  In a “defs” file, forgetting to put in the “module” statement.
8.  In a “defs” file, not putting an “end” statement for the “module” statement.
9.  In a “defs” file, not putting in an “include Test::Unit::Assertions” statement if you are using “assert” statements in the file.

10. Capitalizing of test names instead of keeping them lower case, “test_01_test_to_see_if this_works”.

Links for Ruby – rubyforge the main Ruby site:
http://rubyforge.org/

The easiest Ruby to install on Windows – One Click Ruby: http://rubyforge.org/projects/rubyinstaller/

Watir has two places of interest.  One is on rubyforge: http://rubyforge.org/projects/wtr/

The other is on the openqa.org site: http://openqa.org/