Posts Tagged ‘webrat’

Making your Rails controllers respond

Friday, May 8th, 2009

I was looking at Google Analytics today and found that the following searhes often end up here:

webrat no action responded to /. actions: index

and

"no action responded to" webrat

It’s good to see that people are trying out webrat, presumably with cucumber to test their apps.

The problem here is most likely that you need to add a method to your controller to handle the incoming request.  So if you are getting the message

no action responded to new

you would want to go to the controller that you are testing and add something like this:

1
2
3
def new
    #TODO make new do something
end

Don’t forget to add a view such as app/views/example/new.html.erb that corresponds to the action you just added to your controller.

Also, learn to find the TODO annotation again with rake.

Chores: a test driven website – part 6 (the son of)

Saturday, January 24th, 2009

This is part of the Chores series of posts

Last time we found the warm fuzzy feeling of green tests and a completed feature.   Unfortunately, while that is a great feeling, our website is far from complete.  We can however begin to leverage what we have learned so far to get this project moving.  I write up my next feature for cucumber and save it in the features directory as define_children.feature. 

Story:  Define child
As a parent
I want to define my child(ren)
So that I can assign them chores

Scenario: Defining a child
Given I am on the homepage
When I follow "Add Child"
And I fill in "child[nickname]" with "Bobby"
And I fill in "child[open_identifier]" with "bobby.example.com"
And I press "Add"
Then I should see "Child added."
And I should see "Bobby"

Scenario: Defining a second child
Given I am on the homepage
When I follow "Add Child"
And I fill in "child[nickname]" with "Bobby"
And I fill in "child[open_identifier]" with "bobby.example.com"
And I press "Add"
And I follow "Add another Child"
And I fill in "child[nickname]" with "Suzy"
And I fill in "child[open_identifier]" with "suzy.example.com"
And I press "Add"
Then I should see "Child added."
And I should see "Bobby"
And I should see "Suzy" 

Here are a few things that you can pick out of this feature.

  • It infers  that there is some sort of identity, probably with some sort of authentication at a future point.
  • Identities will likely have roles of parent and child.
  • The fact that the child is associated with an openID confirms that there will be authentication at some point.
  • Identites have either a one-to-many or a many-to-many relationship.

I think (at this point) that I’m going to leave the actual authentication out of the scope of the feature.  However it looks like there will be an Identity and some sort of relationship model in our near future.  Notice also that I took advantage of some of webrat’s built in step defintions.  I considered making a custom step to combine the entering of nickname, openId and pressing add, but I left it with the defaults for now.

Before we go ahead and create the models, let’s follow our feature and see where it leads us.  Cucumber’s output looks like:

$ cucumber features
Story:  Define child  # features/define_children.feature
As a parent
I want to define my child(ren)
So that I can assign them chores
  Scenario: Defining a child      # features/define_children.feature:6
    Given I am on the homepage   # features/step_definitions/chores_steps.rb:1
    When I follow "Add Child"      # features/step_definitions/webrat_steps.rb:8
      Could not find link with text "Add Child" (RuntimeError)
      c:/Ruby/lib/ruby/gems/1.8/gems/webrat-0.3.4/lib/webrat/core/flunk.rb:4:in 'flunk'

So we will need to modify the homepage to have a link to add child and to do that we will need to have a url to go to.   That means we need a controller.  Since the link we are making is to add a child let’s call it the Children Controller. 

$ ruby script/generate controller Children index new create
      exists  app/controllers/
      exists  app/helpers/
      create  app/views/children
      exists  test/functional/
      create  app/controllers/children_controller.rb
      create  test/functional/children_controller_test.rb
      create  app/helpers/children_helper.rb
      create  app/views/children/index.html.erb
      create  app/views/children/new.html.erb
      create  app/views/children/create.html.erb

and update ./config/routes.rb to read

1
2
3
4
5
ActionController::Routing::Routes.draw do |map|
  map.resources :chores
  map.resources :children
  map.root :controller => 'home'
end

and finally update our homepage view (./app/views/home/index.html.erb) to read

views

1
2
<%= link_to "Add Chore", new_chore_path %>
<%= link_to "Add Child", new_child_path %>

Let’s check back with Cucumber and see how we did.

$ cucumber features
Story:  Define child  # features/define_children.feature
As a parent
I want to define my child(ren)
So that I can assign them chores
  Scenario: Defining a child      # features/define_children.feature:6
    Given I am on the homepage      # features/step_definitions/chores_steps.rb:1
    When I follow "Add Child"      # features/step_definitions/webrat_steps.rb:8
    And I fill in "child[nickname]" with "Bobby"      # features/step_definitions/webrat_steps.rb:12
      Could not find field labeled "child[nickname]" (RuntimeError)
      c:/Ruby/lib/ruby/gems/1.8/gems/webrat-0.3.4/lib/webrat/core/flunk.rb:4:in 'flunk'

Not bad at all the step passed and it is now looking for the form on the new view.   Let’s make it by editing /app/views/children/new.html.erb to read:

1
2
3
4
5
6
7
8
9
10
11
12
13
<% form_for(@child) do |f| %>
  <%= f.error_messages %>
  <p>
    <%= f.label :nickname %><br />
    <%= f.text_field :nickname %>
  </p>
 
  <p>
    <%= f.label :open_identifier %><br />
    <%= f.text_field :open_identifier %>
  </p>
  <%= f.submit "Add" %>
<% end %>

Let’s also remove ./app/views/children/create.html.erb file while we are at it, since we won’t need a view for this method.

Hmmm. hold on for a second. We are referencing an instance of @child that doesn’t exist. That’s definately going to fail. We need to add a model to define relationships between identities not to mention the identities themselves.

$ ruby script/generate model Identity identifier:string
      exists  app/models/
      exists  test/unit/
      exists  test/fixtures/
      create  app/models/identity.rb
      create  test/unit/identity_test.rb
      create  test/fixtures/identities.yml
      exists  db/migrate
      create  db/migrate/20090124055031_create_identities.rb

and

$ ruby script/generate model Child parent_id:integer child_id:integer
      exists  app/models/
      exists  test/unit/
      exists  test/fixtures/
      create  app/models/child.rb
      create  test/unit/child_test.rb
      create  test/fixtures/children.yml
      exists  db/migrate
      create  db/migrate/20090124055548_create_children.rb

When we return it will be time to edit the migrations and run them and then dive back into Test::Unit.

If you buy Cucumber and Webrat now, you will also get…

Sunday, January 18th, 2009

When you install Cucumber to your Rails project you get a number of  built in steps out of the box without any additonal effort.  These steps will do a number of common tasks.   Using them is a simple matter of knowing the correct wording.  If you want to see these steps for yourself, open ./features/step_definitions/webrat_steps.rb in your project after you have installed cucumber in your project.

Here are are a number of useful steps that are included with webrat:

  • Submitting a form with a button – Let’s say you have a feature to collect email addresses and you have gotten to the step where the user clicks the button marked “I love Spam” to entrust you with their email address.   To perform the clicking of the spammy button in your cucumber feature simply write the step.

When I press “I love Spam”

  •  Clicking a link – Have a feature that includes clicking a link labeled “Log in” that logs you in to the website?  To click the link in your test simply use the following.

When I follow “Log in”

  • Filling in a field in a form – Let’s say you are writing the website where people can go to register their mouse.  You have a new action on your form controller and the view for new allows the user to fill in the serial number of their mouse to be saved via your Mouse model.   The html generated by this view would name the serial number field something like “mouse[serial_number]“.   To write a feature where you enter a serial number of “I10v3M1c3″ and click a button named “Submit”, you would simply use the following pre-built webrat steps in your feature.

When I fill in “mouse[serial_number]” with “I1ov3M1c3″

And I press “Submit”

  • Verifying that text is displayed – So you want ensure that a message is  sent to the screen that says “Hey smart guy, you entered your gender as a numeric value!”?  Use the following step to make sure that text is being displayed in the response.

Then I should see “Hey smart guy, you entered your gender as a numeric value!”

These are just a few of the pre-built steps available to you that you can use without any step writing at all.  Check them out. 

Chores: A test driven website

Thursday, January 8th, 2009

This is part of the Chores series of posts

I admit it, I don’t always write my tests first, and at other times I don’t write my tests at all. There I said it. However, I have found that writing tests first has accelerated the speed at which I learn and it has improved the quality of my work.

There is a general feeling that people who use TDD see themselves as better than those who don’t use it. I don’t think that this is the case. I think that this happens because once you start working in these styles, it’s strange to work without them. It’s not that you can’t or won’t do it, but it feels like you are working with a reduced toolset when you do.

I’m no expert at test driven development or similar styles, but the more I use them, the harder it is to imagine not being able to design and shape my objects with automated tests. I wouldn’t want to figure out how related objects fit together without the documentation that automated testing provides.

The term test driven fools people into thinking that it’s all about testing, but it’s not. It’s about creating functional designs with reduced dependencies. It’s also about being able to maintain your code. It’s about self improvement.

The point of learning to work in these styles is not to be better than those that don’t. Instead, the point is to make the code you wrote today better than the code you wrote a week ago, or a month ago. It is also so that when you or someone else looks at the code you wrote today, they will find it to be:

  • working
  • documented
  • flexable
  • extendable

There are many articles on the benefits of working in this style, and my point is not to repeat them. In fact my purpose is to prove them correct, or at least to show that it is not difficult to do and just takes a little discipline. The same discipline that I lack at times, but I’m getting better.

The Project

I am the father of 4 kids, and they are wonderful children who make messes and generally don’t clean up after themselves unless told to do so (and sometimes not even then). My general idea of a website is to built a Rails app that a family can go and if they are parents, keep track of what their kids do or don’t do. If they are kids, they can document what they have done and perhaps see much they have earned for the week in allowance. I have tried doing allowances before, but the problem is that my kids have to be told to do what they are supposed to be doing quite often to make sure they are doing it. I don’t want to do this. I want to provide immediate feedback for them on what they are earning, and I am hoping that this will give them the motivation they need to keep going. I also want to help them to develop the habits that will continue with or without the draw of rewards. It’s a moderately involved project,

Test or Die

Because January is Test Awareness Month I have decided to document bulding (at least beginning to build) this site on my blog using Cucumber and Test::Unit to drive it. I am developing currently on a Windows machine, though this may change as the project goes on as my other machines are running Ubuntu. So without further ado…

Note: (May 11, 2009 – I originally created this with Rails v2.2.2. I’m currently running back through to check compatability with Rails v2.3.2)

Start!

$ rails chores
      create
      create  app/controllers
      create  app/helpers
      .....
      create  log/development.log
      create  log/test.log

I have my rails app. The first thing I want to do is get this into source control. In my case git.

$ cd chores

and

$ git init

now I don’t want to store everything in source control. To to make git ignore files and or paths I create a .gitignore file in the chores directory and it looks like this…

tmp*
log*
*[~#]

what this does is it ignores the tmp and log directories and it also ignores any backup files which contain ~ or #.

Now I can check the status to make sure that the .gitignore file is working.

$ git status
# On branch master
#
# Initial commit
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       .gitignore
#       README
#       Rakefile
#       app/
#       config/
#       doc/
#       public/
#       script/
#       test/
nothing added to commit but untracked files present (use "git add" to track)

Followed by

$ git add .
 
$ git commit -am "initial commit"

Which adds my site to git’s index and commits my first commit. Now I’m going to backup my git repository to my shared host. A good explanation of this can be found here.

Now I already have cucumber, webrat and rspec installed as gems. In order to drive my development with cucumber features I need to set up my app to use cucumber.

$ ruby script/generate cucumber
      create  features/step_definitions
      create  features/step_definitions/webrat_steps.rb
      create  features/support
      create  features/support/env.rb
      create  features/support/paths.rb
      exists  lib/tasks
      create  lib/tasks/cucumber.rake
      create  script/cucumber

That does the trick.

Gotchas

I’ve found a couple gotchas at this point when working in Windows. The first is that cucumber doesn’t display the letter ‘a’ in the output if you don’t modify ./features/support/env.rb with the following line:

1
$KCODE=''

also you will want to make sure you are using a version of the sqlite gem that supports windows:

gem install sqlite3-ruby -v=1.2.3

What are your features?

Cucumber Features are a nice way to be able to document what you want your site to do and verify that it does it. Probably the easiest way do show this is to do it. I like to make features that aren’t too broad or two specific. I will start with one that is core functionality and save it in the ./features directory as define_chores.feature

Story:  Define chores
As a parent
I want to define chores
So that I can assign them to my children
 
Scenario: Creating a chore
Given I am on the homepage
When I follow “Add Chore”
And I fill in "chore[name]" with “My first chore”
And I press "Add"
Then I should see “Chore added.”
And I should see “My first chore”

Now we have written our first feature. It is a pretty simple one, but as you can see it describes our intent in readable English. Now let’s fire up cucumber and see what we get.

Update: The following issue was fixed when I tried it in Rails 2.3.2 and cucumber 0.3.1.

$ cucumber features
c:/Ruby/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in `gem_original_require': no such file to load -- webrat/rspec-rails (MissingSourceFile)

Hmmm.. what happened here. Cucumber uses the ./features/support/env.rb file for configuration let’s look there. Sure enough there is a require statement for ‘webrat/rspec-rails’. Let’s google it. Looks like the offending require is to add some webrat matchers to rspec. I’m just going to comment it out for now and try again.

Update: Using the features rake task (rake features from the command line) at this point in Rails 2.3.2 and cucumber 0.3.1 caused the following error:

rake aborted!
no such file to load -- /home/rubyyot/working/chores/db/schema.rb

This is because we have not yet defined any database tables. To fix this we simply have to create an empty file in the correct location. In Windows you can navigate to the folder and create a new file. In Linux you can type from your rails root:

touch db/schema.rb

This time I get:

$ rake features
Story:  Define chores
As a parent
I want to define chores
So that I can assign them to my children
 
  Scenario: Creating a chore                          # features/define_chores.feature:6
    Given I am on the homepage                        # features/step_definitions/webrat_steps.rb:6
      No route matches "/" with {:method=>:get} (ActionController::RoutingError)
      (eval):2:in '/^I am on (.+)$/'
      features/define_chores.feature:7:in 'Given I am on the homepage'
    When I follow “Add Chore”                         # features/define_chores.feature:8
    And I fill in "chore[name]" with “My first chore” # features/define_chores.feature:9
    And I press "Add"                                 # features/step_definitions/webrat_steps.rb:14
    Then I should see “Chore added.”                  # features/define_chores.feature:11
    And I should see “My first chore”                 # features/define_chores.feature:12
 
1 scenario
1 failed step
1 skipped step
4 undefined steps
 
You can implement step definitions for undefined steps with these snippets:
 
When /^I follow “Add Chore”$/ do
  pending
end
 
When /^I fill in "([^\"]*)" with “My first chore”$/ do |arg1|
  pending
end
 
Then /^I should see “Chore added\.”$/ do
  pending
end
 
Then /^I should see “My first chore”$/ do
  pending
end
 
rake aborted!
Command failed with status (1): [/usr/bin/ruby1.8 -I "/usr/lib/ruby/gems/1....]
 
(See full trace by running task with --trace)

So what happened here?

Cucumber loads up all the features and all of the steps and then uses regular expressions to match the features to the steps. Then it executes the code associated with each step in order. Notice that it knew what “Given I am on the homepage” meant. That is because Webrat has already pre-loaded some common rails testing steps for us.

So what error are we having here? It says “No route matches “/” with {:method=>:get}”.

What are routes?

Routes in Rails have some pretty nifty tricks up their sleeves. Routes map an incoming request to an action on a controller. They live in config/routes.rb where you will find some good auto-generated documentation on how to configure them. In this case we need to create a route for our home page. This is accomplished by adding

  map.root :controller => 'home'

somewhere after

ActionController::Routing::Routes.draw do |map|

and before

end

This tells rails that any requests that come in for the base url should be routed to the HomeController. Next time we can start working on building this object.