Ruby for Newbies: Testing Web Apps with Capybara and Cucumber
videos

Ruby for Newbies: Testing Web Apps with Capybara and Cucumber

Tutorial Details
  • Topic: Cucumber, Capybara, Rspec
  • Difficulty: Easy
  • Estimated Completion Time: 30 mintues

Ruby is a one of the most popular languages used on the web. We’re running a Session here on Nettuts+ that will introduce you to Ruby, as well as the great frameworks and tools that go along with Ruby development. In this episode, you’ll learn about testing your Sinatra apps with Cucumber, Capybara, and Rspec.

In the previous tutorial in this series, we looked at Rspec, and how you can do test-driven development with it. I mentioned that, in the next episode, we’d look at using Rspec to test web apps. However, a couple of commenters mentioned they’d be interested in seeing testing with Cucumber, so that’s what we’ll use to test a very simple web app instead. Actually, we’ll be using part of Rspec to get the same matcher syntax as you saw last time, but the actual test setup is in Cucumber.


Prefer a Screencast?

Press HD for the clearest picture.
Subscribe to our YouTube and Blip.tv channels to watch more screencasts.

Step 1: Building the App

We’re going to create an incredibly simple Sinatra app to test. For starters, let’s create a project folder and throw this in a Gemfile:

What? You don’t know Gemfiles? Get out from under that rock and catch up with episode 8

source :rubygems

gem "sinatra"
gem "shotgun"
gem "cucumber"
gem "capybara"
gem "rspec"

Now, run bundle install in that directory. With the gems installed, we’re ready to go! (Optionally—if you’re using RVM—you could install these gems for this project only by running rvm --rvmrc --create 1.9.2@cucumber_example; run this before bundle install)

So, open a file called myapp.rb; here’s our super simple app; it just simulates a site that might let you sign up for a newsletter.

require "sinatra/base"

class MyApp < Sinatra::Base
    get "/" do
        erb :index
    end
    
    post "/thankyou" do 
        @name = params["name"]
        @email = params["email"]
        erb :thankyou
    end
    
    get "/form" do
        erb :form
    end
end

If you’re not familiar with Sinatra, check out Dan Harper’s excellent sessions Singing with Sinatra; that’ll get you up and running with the basics in no time.

If you are familiar with Sinatra, you’ll see that we’re creating three paths here; on the home page (’/’), we just render the index.erb template (more on the templates in a minute). If we get a post request to the path /thankyou, we take the values of the name and email parameters and assign them to instance variables. Instance variables will be available inside whatever template we render, which happens to be thankyou.erb. Finally, at /form, we render the form.erb template.

Now, let’s build these templates. Sinatra will look inside a ‘views’ folder for the templates, so let’s put them there. As you saw in myapp.rb, we’re using ERB to render the templates, so they’ll, of course, be ERB templates. If we have a layout.erb template, it will wrap all our other templates. So, let’s do this:

layout.erb

<!DOCTYPE html>
<html>
<head>
  <meta charset='UTF=8' />
  <title>THE APP</title>
</head>
<body>

  <h1>THE APP</h1>

  <%= yield %>

</body>
</html>

That call to yield will be where the other templates are inserted. And those other templates are pretty simple:

index.erb

<p>This is the home page</p> 
<p><a id="link" href="/form">Sign up for our newsletter!</a></p>

form.erb

<form method="post" action="/thankyou">
    <p>
        Fill out this form to receive our newsletter.
    </p>
    <p>
        <label for="name">Name:</label>
        <input type="text" name="name" id="name" />
    </p>
    <p>
        <label for="email">Email:</label>
        <input type="text" name="email" id="email" />
    </p>
    <p>
        <button type="submit">Sign Up!</button>
    </p>
</form>

thankyou.erb

<p>Hi there, <%= @name %>. You&#39;ll now receive our email at <%= @email %></p>

So, there’s our app. To test it manually, you can put this in a config.ru file:

require "./myapp"
run MyApp

And then run shotgun in the terminal. This will start up a websever, probably on port 9393. You can now poke around and test our web app. But we want to automate this testing, right? Let’s do it!


Step 2: Setting our our Test Environment

Cucumber bills itself as “behaviour driven development with elegance and joy.” While joy seems a bit far-fetched to me, I think we’ll both agree that elegance, well, Cucumber’s got it.

Because behaviour driven development is partly about understanding what the client wants before you begin coding, Cucumber aims to make its tests readable by clients (AKA, non-programmers). So, you’ll see here that all your tests are written in what appears to be plain text (it’s actually Gherkin).

Remember how, with Rspec, we has separate spec files to describe different functionalities? In Cucumber-speak, those are features, and they all belong in a “features” folder. Inside that folder create two more folders called “support” and “step_definitions.”

Inside the “support” folder, open an env.rb. This code will set up our testing environment. Here’s what we need:

require_relative "../../myapp"

require "Capybara"
require "Capybara/cucumber"
require "rspec"
World do
  Capybara.app = MyApp

  include Capybara::DSL
  include RSpec::Matchers
end

This requires the different libraries that we need, and uses include to load their methods into our environment. What’s this Capybara that we’re using? Basically, it’s the functionality that allows us to use our web app, so that we can test it. It’s important to set Capybara.app to our app. I should mention that, were we doing this with a Rails app, most of this setup would be done automatically for us.

(Note: in the screencast, I include RSpec::Expectations unneccessarily; leave it out.)

Okay, so, let’s write some tests!


Step 3 Writing the Tests

Let’s start with our home page. Open the file home_pages.feature (in the “features” folder) and start with this:

Feature: Viewer visits the Home Page
  In order to read the page
  As a viewer
  I want to see the home page of my app

This is a common way to start a feature file starts; Doesn’t really look like code, does it? Well, it’s Gherkin, a domain-specific languages (DSL) that “lets you describe software’s behaviour without detailing how that behaviour is implemented.” What we’re written so far doesn’t run in any way, but it explains the purpose of the feature. Here’s the general structure:

In order to [goal]
As a [role]
I want [feature]

You don’t have to follow that template: you can put whatever you want; the purpose is to describe the feature. However, this seems to be a common pattern.

Next comes a list of scenarios that describe the feature. Here’s the first:

Scenario: View home page
  Given I am on the home page
  Then I should see "This is the home page."

Each scenario can have up to three parts: Givens, Whens, and Thens:

  • GivenGiven lines describe what pre-condition should exist.
  • WhenWhen lines describe the actions you take.
  • ThenThen lines describe the result.

There are also And lines, which do whatever the line above them does. For example:

Given I am on the home page
And I am signed in
Then I should see "Welcome Back!"
And I should see "Settings"

In this case, the first And line acts as a Given line, and the second one acts as a Then line.

We’ll see a few When lines shortly. But right now, let’s run that test. To do that, run cucumber in the terminal. You’ll probably see something like this:

Cucumber feature files are written to be readable to non-programmers, so we have to “implement step definitions for undefined steps.” Thankfully, Cucumber gives us some snippets to start with.

Looking at these snippets, you can see how this will work. Each step is matched with a regular expression. Any quoted values will be captured and passed as a block parameter. Inside the block, we do whatever we expect to happen as a result of that step. This might be set-up code in Given steps, some calculations or actions in When steps, and a comparison in Then steps.

Cucumber will load any files in the folder “features/step_definitions” for steps, so let’s create “sinatra_steps.rb” file and add these two steps:

Given /^I am on the home page$/ do
  visit "/"
end

Then /^I should see "([^"]*)"$/ do |text|
  page.should have_content text
end

In this little snippet here, we’re using Cucumber, Rspec, and Capybara. Firstly, we’ve got the cucumber Given and Then method calls. Secondly, we’re using the Capybara methods visit (to visit a URL) and has_content?. But you don’t see the call to has_content? because we’ve loaded the RSpec matchers, so we can make our tests read as they would with Rspec. If we wanted to leave RSpec out, we would just write page.has_content? text.

Now, if you run cucumber again, you’ll see that our tests pass:

Let’s add two more Scenarios for our home page:

Scenario: Find heading on home page
  Given I am on the home page
  Then I should see "MY APP" in the selector "h1"

Scenario: Find the link to the form
  Given I am on the home page
  Then I should see "Sign up for our newsletter." in a link

These require two more Then steps, as you’ll find if you try to run this. Add these to sinatra_steps.rb:

Then /^I should see "([^"]*)" in the selector "([^"]*)"$/ do |text, selector|
  page.should have_selector selector, content: text
end

Then /^I should see "([^"]*)" in a link$/ do |text|
  page.should have_link text
end

You should be able to tell what these are doing: the first looks for text within a certain element on the page. The second looks for a link with the given text (yes, you could have done Then I should see "Sign up ..." in the selector "a", but I wanted to should you another Capybara/Rspec method)

Again, run cucumber; you’ll see all our tests passing:

Let’s now open “features/form_page.feature”; throw this in there:

Feature: Viewer signs up for the newsletter
  In order to recieve the newsetter
  As a user of the website
  I want to be able to sign up for the newsletter

  Scenario: View form page
    Given I am on "/form"
    Then I should see "Fill out this form to receive our newsletter."

  Scenario: Fill out form
    Given I am on "/form"
    When I fill in "name" with "John Doe"
    And I fill in "email" with "john@doe.com"
    And I click "Sign Up!"
    Then I should see "Hi there, John Doe. You&#39;ll new receive our email newsletter at john@doe.com"
    

The first scenario here is pretty simple, although we need to write the Given step for is. You can probably figure out how to do that by now:

Given /^I am on "([^"]*)"$/ do |path|
  visit path
end

The second one is a little more in depth. For the first time, we’re using When steps (remember, the And steps that follow the When step are also When steps). It’s pretty obvious what those When steps should do, but how do we do that in the Ruby code? Thankfully, Capybara has a few handy methods to help up:

When /^I fill in "([^"]*)" with "([^"]*)"$/ do |element, text|
  fill_in element, with: text
end

When /^I click "([^"]*)"$/ do |element|
  click_on element
end

We’re using the fill_in method, which takes the name or id attribute of an element on the page. We’re also using click_on, which will click on the element with the given text, id, or value. There are also the more specific click_link and click_button. To see more, check out the Capybara Readme. Browse around the “DSL” section to see more of the methods that Capybara offers.

When you run cucumber now, you should get all our tests, passing:


Conclusion

Realize that what we’re testing here is the just UI, not the underlying code. If our code really signed you up for the newsletter, we’d also have to test the code that adds the name and email address to our database, etc. Just because we see what we’re supposed to see, doesn’t mean we’ll actually receive the newsletter, right? That should be tested separately.

And that’s testing web apps with Cucumber, Capybara, and Rspec matchers. As I mentioned, check out the Capybara docs for more!

Note: Want to add some source code? Type <pre><code> before it and </code></pre> after it. Find out more
  • http://www.notes.kloop.kg Said

    Thanks ))) great post for beginners )))

  • Wayne

    There’s a typo in the Gemfile snip above; “shotgun” (not shotgum”)

  • David

    To see a tutorial on cucumber from you guys is incredible and I thank you immensely. I can’t wait to give it a whirl!

  • Me

    First, thank for the nice tutorial !
    -But wouldn’t it be better, to write first the Tests and then the code ?

    PS:
    There is a new book about Cucumber:
    http://pragprog.com/book/hwcuc/the-cucumber-book

  • http://zackperdue.com zack

    You guys have a double word in one of the titles “Step 2: Setting our our Test Environment”. Your Welcome. :)

  • ck3g

    I also like the “Ruby Resource” section. Which is missing in this tutorial =)

    • http://andrewburgess.ca Andrew Burgess
      Author

      Check out the Cucumber websites (http://cukes.info) to learn more about Cucumber; they’ve got a lot of videos there that you might find useful. There’s also the Cucumber Book, which isn’t free and which I haven’t had a chance to read yet.

      And there’s your Ruby Resource of the week :)

      • http://mkltesthead.com/ Michael Larsen

        Note: that “The Cucumber Book”, which has been in Beta the past few months, recently shipped to production. If you pick up the Beta e-book version now, you’re effectively getting the 1.0 production version :). It’s a really good resource, well written, and plenty of underlying meat so that you understand the full process, both from testing command line as as well testing web apps with capybara, etc. there’s Also “the Secret Ninja Cucumber Scrolls” which shows Cucumber in three different environments (Ruby, Java and .NET).

  • Joe Turner

    Shouldn’t this be part of the Ruby for Newbies series?

  • http://8gramgorilla.com 8 Gram Gorilla

    I have mixed feelings about testing. Yes, I can completely and utterly understand the merits and necessity of functional code and usability testing it’s just that I’ve never seen any real practical examples of it being conducted. I mean, the tests outlined above are nice but are they really necessary to write? For example, you’re testing the UI as you said and that’s something a user could test manually in about three seconds.

    I just feel kinda strange – I can’t decide if I’m being ignorant and missing out on something hugely important or if most of the testing hype that’s going on right now is a case of the Emperors New Clothes.

    Great article, btw. More RoR posts please! :D

    • http://mkltesthead.com/ Michael Larsen

      8 Gram Gorilla, there are various opinions on both sides, and I’ve worked in environments that have done traditional development as well as TDD/BDD development (and I’m a tester, not a developer per se). I can say from my own experiences that shops that have invested the time to do TDD/BDD do develop code that is more solid, but it’s a different way of thinking, one where a lot of testing is done up front with the code. Do I still see problems? Sure, but a lot fewer catastrophic problems in comparison to traditional development environments. That alone IMO makes the extra testing worth it.

    • Andy Hamilton

      Tests (for me at least) are more useful when you get later on in the development. Sure at the beginning it might take less time to check when you just want to see a form submits and outputs ‘hello’ but once you’ve got a big application you find that testing things like this:

      User signs up -> inputs incorrect information -> shows error -> corrects info and signs in -> receives an email -> clicks the email link -> changes password -> clicks email link etc etc etc

      becomes cumbersome (get it?) and writing the tests and running them to perfect that process becomes a LOT faster than doing it manually.

  • William

    How you get that beautiful color highlighting in the Terminal.app? I want it badly!!!

  • Nate Hunzaker

    This was an amazing tutorial, I fully enjoyed it.

    I know this is super picky but:

    Feature: Viewer signs up for the newsletter
    In order to recieve the newsetter
    As a user of the website
    I want to be able to sign up for the newsletter

    “i” before “e” except after “c”, and a typo :).

  • http://www.cdbaby.com/cd/solidstatecrew APS

    Any way to get this app running in Windows? I didn’t realize shotgun was not supported on Windows (newbie). Thanks!

  • http://www.music-explained.com john barnes

    Great tutorial for a capybara newb, much appreciated.

    I also agree that its feels more useful to write the tests first, before the production code.

  • Nick

    Great tutorial! Gives a good understanding of what Rails actually does in the background too. I just find it kind of funny that you managed to mis-type the thankyou page on your view and your test, “Hi there, <%= @name %>. You’ll new receive our email at <%= @email %>”

    Nonetheless, very descriptive and quite educational!

  • Jun

    The content in the index.erb should be “This is the home page.” the period is missing.

  • Jun

    Again, the text for “Sign up for our newsletter.” should be “Sign up for our newsletter!”

    By the way, on Mac Lion with ruby 1.9, I have to disable
    # include Capybara::DSL
    # include RSpec::Matchers

    to return the error quicker. Otherwise, I will see “stack level too deep” error.

    • Philip

      Thanks for the information. After deleting those 2 lines, I got error results very quickly.

  • boraaymete

    Excellent explanation! Thanks for posting.