back

Rails Cucumber / Machinist Tutorial: Machinist with Cucumber in 10 minutes

Update

A newer version of this rails and cucumber tutorial, using rails 3 and ruby 1.9 is available here.

I’ve had a lot of traffic for machinist, but my earlier post isn’t very useful for actually using it.

So, here’s how to start using machinist with cucumber in a rails app. I’ll use a simple app that deals with strawberries.

Start a rails app

1
2
3
4
5
6
7

sudo gem install rails --source=http://gems.rubyonrails.org
rails strawberries
cd strawberries
script/generate nifty_layout # this is from the nifty_scaffold gem, which I recommend.


Grab the gems

1
2
3
4
5
sudo gem install cucumber --no-ri --no-rdoc
sudo gem install notahat-machinist --no-ri --no-rdoc

# if this doesn't work, try doing:
# gem sources -a http://gems.github.com 

Then add them to your test environment, so that other folks on your team can easily get them.

1
2
3
4
5
6
# config/environments/test.rb

config.gem 'cucumber'
config.gem 'notahat-machinist', :lib => 'machinist'
config.gem 'faker'

Generate the files

From your rails app’s root, enter:
1
2
script/generate cucumber
touch features/support/blueprints.rb

Write a story

Now that we have our environment setup, let’s add the stories. These are just sketches of how our app should behave.

1
2
3
4
5
6
7
8
9
10
11
12
13
# features/strawberries.feature

  In order to live
  people
  want to be able to eat strawberries
  
  Scenario: Eating strawberries
    Given a strawberry that is "blue"
    When I go to the homepage
    Then I should see "There is 1 strawberry"
    When I follow "eat the blue strawberry"
    Then I should see "Strawberry eaten!"

Implement steps

It’s a good idea to implement steps first, so that you can have failing steps to work with. Cucumber will provide step definitions for us, so just enter:

1
2
3

rake features

Now we can add the step definition for the strawberries.

1
2
3
4
5
#features/step_definitions/strawberry_steps.rb
Given /^a strawberry that is "(.*)"$/ do |color|
  Strawberry.make(:color => color)
end
  

Now let’s try rake:features again. This time we get

1
uninitialized constant Strawberry (NameError)

Which means it’s time to start adding code.

Implement feature and add blueprints

Now that we’ve written some tests, we can go ahead and build this.

1
2
3

script/generate nifty_scaffold strawberry color:string
rake db:migrate

Again, I’m using the nifty generator, which saves lots of time for making quick demo apps.

Also, we’re going to want to add a strawberry blueprint, so that we can use Machinist.

So, again, let’s try

1
2
3

rake features

This time we get No blueprint for class Strawberry (RuntimeError).

So, let’s add a blueprint:

1
2
3
4
5
6
7
8
#features/support/blueprints.rb

Sham.color { Faker::Lorem.words(1).first.downcase }

Strawberry.blueprint do
  color {Sham.color}
end

This gives us the ability to say Strawberry.make, which will create a new strawberry with a random color. The nice part about machinist is that we can do both Strawberry.make and Strawberry.make(:color => ‘red’), which becomes really useful when you have a model with a lot of validations that you don’t want to have to specify every time. Machinist lets you focus on specifying only what’s important to the specific test.

Now, at this point, since I used the nifty_scaffold gem, we just have to change some text to make the feature pass, and we’re done.

So, now that we have a blueprint, let’s try rake:features again. We get undefined method `root_path’ for # (NoMethodError).

So, to fix this, let’s add a root path.

1
2
3
4
# config/routes.rb

map.root :controller => 'strawberries'

And then try rake features again. This time we get:

1
2
3
4
5
6

Then I should see "There is 1 strawberry"  # features/step_definitions/webrat_steps.rb:89
      expected: /There is 1 strawberry/m,
           got: "<! #etc etc


So, what’s happening here is that cucumber/webrat are going to our home page, but they aren’t finding the text that we wrote that we wanted to see in the story.

So, let’s change it.

1
2
3
4
5

# app/views/strawberries/index.html
<h1>There is <%= "#{Strawberry.count}" -%> strawberry</h1>


Now if we try rake features again we get Could not find link with text or title or id “eat the blue strawberry” (Webrat::NotFoundError)

So we add that link

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

<h1>There is <%= "#{Strawberry.count}" -%> strawberry</h1>
<table>
  <tr>
    <th>Color</th>
  </tr>
  <% @strawberries.each do |strawberry| %>
    <tr>
      <td><%=h strawberry.color %></td>
      <td><%= link_to "Show", strawberry %></td>
      <td><%= link_to "Edit", edit_strawberry_path(strawberry) %></td>
      <td><%= link_to " eat the #{strawberry.color} strawberry", strawberry, 
:confirm => 'Are you sure?', :method => :delete %></td>
    </tr>
  <% end %>
</table>

If I try rake:features again, I see that now only the last step is failing. Then I should see “Strawberry eaten!” # features/step_definitions/webrat_steps.rb:89 expected: /Strawberry eaten!/m,

So, let’s fix that too.

1
2
3
4
5
6
7
8
9
10
# app/controllers/strawberries_controller.rb

  def destroy
    @strawberry = Strawberry.find(params[:id])
    @strawberry.destroy
    flash[:notice] = "Strawberry eaten!" #change the flash
    redirect_to strawberries_url
  end


So, now we have 5 passing steps

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

momoro:~/Projects/Rails/strawberries$ rake features
(in /Users/mischa/Projects/Rails/strawberries)
In order to live  # features/strawberries.feature
  people
  want to be able to eat strawberries
  Scenario: Eating strawberries                # features/strawberries.feature:5
    Given a strawberry that is "blue"          # features/step_definitions/strawberry_steps.rb:2
    When I go to the homepage                  # features/step_definitions/webrat_steps.rb:6
    Then I should see "There is 1 strawberry"  # features/step_definitions/webrat_steps.rb:89
    When I follow "eat the blue strawberry"    # features/step_definitions/webrat_steps.rb:14
    Then I should see "Strawberry eaten!"      # features/step_definitions/webrat_steps.rb:89


1 scenario
5 steps passed

Machinist note

So, hopefully this is enough for you to get started with. The value of machinist really comes later, when you have a model with more than 1 field, and when you need to make a bunch without wanting to specify everything every time. For example:

1
2
3
4
5
User.blueprint do
  password {'password'}
  password_confirmation ['password'}
  name {Sham.name}
end

here, it would be annoying to have to specify the passowords each time, so it’s easier to let machinist do it, so that you can just say User.make, or User.make(:name => ‘mischa’).

Cucumber note

As you write more features, it will become annoying to run all of them constantly. To run only one feature at once use rake features FEATURE=features/feature_name.feature

Alternatively, use autotest with cucumber by adding “export AUTOFEATURE=true” to your bash profile.

March 08, 2009

  1. jeremy says:

    Sweetness! Thanks for the tips on machinist in cucumber.
    On an aside, I made a rake task to simplify the rake command to run 1 or more features as I had to type '--require features --require lib' every time I ran a single feature or it wouldn't work.

    1
    2
    
    # http://gist.github.com/78185 
    rake features:isolated features/site_feature/use_site.feature:10 
    

  2. Eric says:

    Thanks for the help, the example worked great!

  3. elecnix says:

    When I'm getting a Webrat::NotFoundError, I would like to see the actual HTML Webrat got. How do I do that?

  4. elecnix says:

    I'm reproducing here the response you sent me by e-mail. Maybe others will find it useful.

    You invited me to upgrade my webrat version (I'm already at 0.4.3) or to use your plugin:

    http://github.com/mischa/cucumber_rails_debug/tree/master

    To use it, just put "Then what" inside of any feature, and it will show you the html that webrat sees at that moment.

  5. steve says:

    Check out Dr. Nic's tmbundle for Machinist. It examines your AR models and creates skeletal blueprints in spec/blueprint.rb.

  6. Mischa says:

    @steve, great tip! I actually don't use textmate unfortunately (i'm a die hard vim guy), but definitely something to look at for those who do.