Integration Testing Setup with RSpec 2 and Capybara

NOTE: This is an update to a post I made a while back on using Capybara with RSpec 1 for integration testing. This post is specifically for using Capybara with RSpec 2! The setup information is different but the example is the same.


RSpec 2 has introduced a ton of great new features to make our spec suite better. Writing integration specs in RSpec is easier now, too. Well, maybe not easier, but definitely prettier.

If you already have RSpec integration specs in your project and are upgrading from RSpec 1, then you will want to start by removing any requires or includes from your spec_helper.rb or support/capybara.rb files. Those inclusions are still necessary, but have a new home now.

Next up, put the necessary requires in your Gemfile.

Run a quick bundle install and you should be good to go from a gem perspective.

Run rails generate rspec:install to generate the basic rspec setup for the new version. Note that the spec_helper.rb has changed, so make sure you merge any changes you had into the new file.

Now we need to add the piece that ties RSpec and Capybara together for your integration tests. Paste this file into spec/support/integration_example_group.rb. All the Capybara requires and includes happen here, so you no longer need to do it in your spec_helper.rb or support/capybara.rb.

Just to make sure all this is working properly, go ahead and run rake. If this doesn’t work, you need to fix whatever the problem is before moving on.

Now on to Capybara! By default it uses the rack-test driver, which is similar to webrat in that it is headless and does not run javascript. Unless you have a very javascript intensive app, the rack-test driver should handle the majority of your test cases. The really cool thing about Capybara is that you can use another driver like selenium when you need javascript and rack-test when you don’t.

Now let’s test a simple sign in process. Create a file called spec/integration/guest_signs_in_spec.rb and fill it in like so. Be sure to change the field, button, and link labels to match your actual screens.

That is a trivial example and you would typically want to make more numerous and comprehensive asserts than that, but it should get you going with your first test.

Now that you have a modern integration test setup, I expect to see the quality of your app improve dramatically! Happy TDD’ing!

Comments [2]

Fabrication

Github Repo: http://www.github.com/paulelliott/fabrication

One of my takeaways from RailsConf this year was inspiration to write a new gem. I was sitting at a fast testing round table discussion and people kept bringing up the use of build versus create in factories.

That gave me the idea to lazily generate activerecord associations. Doing that allows you to define your entire object graph in your Fabricators and only generate the pieces you need for the spec you are running. When the association is first accessed, the Fabricators will generate the defined objects, persist them to the database, and return the set as if they were there to begin with. While I was at it, I figured it would be handy to be able to generate regular old ruby objects and Mongoid documents, so I baked in support for doing that as well!

Let’s say we have a Person object that is just a regular old class in Ruby. We can define a Fabricator with static or evaluated attribute values as seen below. You can then Fabricate as many as you want and override specific fields as you go.


class Person; attr_accessor :first_name, :last_name, :age, :friends end

Fabricator(:person) do
  first_name “Joe”
  last_name { Faker::Name.last_name }
  age { rand(100) }
  friends(:count => 10) { |index| Fabricate(:person) }
end

person = Fabricate(:person, :last_name => “Schmoe”)

Notice that you don’t need to pass a parameter to the block. I figured that would just be extra and unnecessary syntax.

Also note the :count parameter. When generating a collection, this allows you to tell Fabrication how many of the object you want it to generate. It will automatically build that many and return an array of the new objects. The block receives the index of the generation as a parameter in case you need it.

If we want to get a little bit more fancy, we can generate active record models as well.

class Company < ActiveRecord::Base
  has_many :divisions
  belongs_to :location
end

Fabricator(:company) do
  divisions(:count => 3) { |company, index| Fabricate(:division, :company => company) }
  after_create { |company| company.update_attribute(:location, Fabricate(:location) }
end

company = Fabricate(:company)
# At this point, we have a location but no divisions.
Division.count
# => 0, because they haven’t been created.
company.divisions.any?
# => true, because the divisions get created when the “getter” for the association is called.
Division.count
# => 3, because now they exist!

Of course, with great power comes great responsibility. This feature is very handy but can also be very dangerous. It is something you need to keep in mind as you are writing your specs.

And finally, you can generate Mongoid Documents very easily.


class Author
  include Mongoid::Document
  field :name
  field :books => Array
end

Fabricator(:author) do
  name { Faker::Name.name }
  books(:count => 5) { Fabricate(:book) }
end

author = Fabricate(:author)

It is that simple! I have been successfully using this in a new Rails 3/Mongoid project I am working on. It won’t step on the toes of other frameworks, so you can also use it alongside your existing factory_girl or machinist implementation.

Please check it out, try it out in your projects, and let me know what you think! Also report any bugs or feature requests you find!

Comments [0]

Sorry, Everyone!

I really messed up recently and I want to apologize to anyone who may have been misinformed or mislead by me. I have no idea how I came to think this nugget of unwisdom that I have been repeating. I must have read an article on it somewhere that was either wrong or maybe I just misunderstood and then began to propagate some misinformation. I have no one to blame but myself though and I feel really terrible and embarrassed about it.

It came out today during Hashrocket's live book club. I was explaining an issue I found in a codebase today where they were using contexts in jQuery to scope selectors. I was explaining that when you pass in a jQuery object as a context, the selector function ignores this parameter as it is expecting a raw DOM object.

I was called out during my monologue and as it turns out, I was dead wrong about it. One of the folks sitting in the circle looked up the jQuery API to get some more context for what I was talking about and noticed that the docs said something contrary to what I was saying. I tested it after the book club and of course it works just like the api docs say it does.

It gets even worse because I have talked about this in numerous blog posts, presentations, and conversations with other developers. The good news is that what I was saying you should do is valid, so anyone listening to me would still be writing working code.

The real problem here is that I took something on faith and didn't look it up or research it for myself. The takeaway is that reading the source code for libraries is critical. Not only will you understand exactly how the code we depend on every day works, but I guarantee you will learn something. I failed to do that in this instance and it caused quite a problem. At the very least, read the API docs before you go running your mouth.

So, to set the record straight, when you are using a context in jQuery you can pass in a raw DOM element, a jQuery object, or a Document and it will work as you expect.

Again, I am deeply sorry for confusion this has caused.

Comments [1]

Making Your Test Suite Scream with Specjour

You know all those other Macs you have lying around the house sitting idle all the time? Well I have some great news. You can finally put those freeloaders to work! With specjour ( http://github.com/sandro/specjour ), you can distribute your test suite across all the cores on your network!

It uses bonjour to communicate and can support multiple projects simultaneously. As long as the machine has the dependencies required for the test suite (gems and unix packages), it can participate in the testing goodness. The current contents of your project get rsync’d to the managers, so they will always match your local changes.

You get started by installing it into your Rails project. First, setup the dependency in the test group of your Gemfile.

1 group :test do
2   gem ‘specjour’, ‘0.2.5’
3 end

Now make a small update to your config/database.yml. A number needs to be appended to the test database name to support multiple simultaneous tests for the same project. It matches that used by parallel_test, so if you used that previously you should be all set.

1 test:
2   database: blog_test<%=ENV['TEST_ENV_NUMBER']%>

To fire up a local manager, open up a new terminal tab and simply run:

1 specjour

To execute your test suite, run:

1 rake specjour

It will automatically determine the number of cores on your machine and utilize them all. With it working on your main dev machine, you are all ready to get the real magic started!

SSH to another machine on your network that you want to run the tests for you. Make sure you install the dependencies your project needs (mysql, imagemagick, mongodb, etc) and all the gems it needs. Run specjour just like you did on the first machine.

Back on your dev machine, run rake specjour again. This time you will see more workers in the list and your suite should run considerably faster.

There are additional options you can pass to specify project names to listen for and how many cores to use, but for most people the defaults should work fine. Those options are passed to the manager and would look something like this:

1 specjour --workers 4 --projects blinq,workbeast

We have seen drastic improvements in the execution times of long running test suites using specjour. But even if your suite only takes a couple of minutes to run and you only have one machine, you should still seen a nice performance gain.

Comments [0]

That Pesky Git Error Message

If you are using git on OSX, then you have undoubtedly run into this error message at one point or another:

”.git/COMMIT_EDITMSG” 69L, 2767C written
error: There was a problem with the editor 'vim'.
Please supply the message using either -m or -F option.

This happens when you do a git commit but accidentally type :Wq when you attempt to write the file. It then bitches that you didn’t type a valid command and so you type the correct command, :wq, only to be greeted by this message in the terminal.

You do have MacVim installed though, right? If not, go here and grab it: http://code.google.com/p/macvim/. Be sure to put mvim in your path so you can launch it from the command line.

With MacVim all set up, correcting this issue is actually very simple. All you need to do is symlink vim to mvim and you will never see the error again.
I put mvim in /usr/local/bin, so to create the symlink I typed:

ln -s /usr/local/bin/mvim /usr/local/bin/vim

You can put it anywhere you want as long as it is before /usr/bin/vim in your path.

 

Comments [0]

Integration Testing with Capybara and Selenium

You may not realize this, but your javascript is full of bugs. Because of the ghetto nature of most applications’ javascript, it is easy to break things that seem unrelated to what you are currently working on. It can be tough to know what exactly is acting on what parts of your site, depending on who did it, when it was written, what libraries were used, etc.

A few options exist for javascript-enabled integration testing, but the easiest to set up and most reliable is still selenium. With capybara, it is simply a matter of tweaking some settings and turning it on where you need it.

To set up selenium, you can start by adding database_cleaner to the test group in your Gemfile.

gem 'database_cleaner', ‘0.5.2’

Now you need to add the following code to spec/support/database_cleaner.rb.

require 'database_cleaner'

Spec::Runner.configure do |config|

  DatabaseCleaner.strategy = :truncation

  config.before :each do
    Capybara.reset_sessions!
    DatabaseCleaner.clean
  end

end

Then, you need to turn off transactional fixtures in your spec_helper.rb.

config.use_transactional_fixtures = false

To set up a specific test to use the selenium driver, you just need to put before and after blocks at the start of your test.

before(:all) { Capybara.current_driver = :selenium }
after(:all) { Capybara.use_default_driver }

Now when you run this test, you will see a Firefox window pop and the browser will dance for you. You shouldn’t need to change anything else in your tests for it to work thanks to the capybara dsl.

Comments [4]

Integration Testing Setup with RSpec and Capybara

Integration testing is critical for modern web applications. It provides a chance to exercise all the features of your site in a real or mock browser and make sure that everything still work correctly with any changes made. It plays a key role in insuring the quality of your application and that your users have as bug-free an environment as possible.

Integrating it into your existing rspec setup is a breeze, too. I am a big fan of Jonas Nicklas’ Capybara (http://github.com/jnicklas/capybara), which is a great gem that lets you mix and match web drivers over a common interface and allows you to test specific functions of your site with the appropriate platform. You can easily use it directly with RSpec to write a comprehensive integration test suite for your application.
Let’s get started by running rake. If you don’t have a passing test suite right now, then shame on you! You need to fix that before doing anything else. If you don’t have a test suite at all, then...well...thanks for reading this and hopefully you will get off to a good start when you’re done.

Once you are all green, setup the latest capybara and rspec gems. Update your Gemfile with the necessary includes.

source :gemcutter

...

group :test do
  gem 'capybara', '0.3.7'
  gem 'rspec-rails', '1.3.2'
  gem ‘factory_girl’, ‘1.2.4’

  ...
end

Run bundle install --relock. If you are updating or installing RSpec for the first time, be sure to run the generator to get your configuration up to date:

script/generate rspec

One last piece of setup, we need to update the spec_helper.rb with the settings for Capybara.

require 'capybara/rails'
require 'capybara/dsl'

Spec::Runner.configure do |config|
  config.use_transactional_fixtures = true
  config.use_instantiated_fixtures = false
  config.fixture_path = RAILS_ROOT + '/spec/fixtures/'

  config.include(Capybara, :type => :integration)
end

Just to make sure all this is working properly, go ahead and run rake again. If this doesn’t work, you need to fix whatever the problem is before moving on.

Now on to Capybara! By default it uses the rack-test driver, which is similar to webrat in that it is headless and does not run javascript. Unless you have a very javascript intensive app, the rack-test driver should handle the majority of your test cases. The really cool thing about Capybara is that you can use another driver like selenium when you need javascript and rack-test when you don’t.

Now let’s test a simple sign in process. Create a file called spec/integration/guest_signs_in_spec.rb and fill it in like so. Be sure to change the field, button, and link labels to match your actual screens.

require ‘spec_helper’

context ‘as a guest on the sign in page’ do

  #Make sure your factory generates a valid user for your authentication system
  let(:user) { Factory(:user) }

  #Browse to the homepage and click the Sign In link
  before do
    visit root_path
    click ‘Sign In’
  end

  context ‘with valid credentials’ do

    #Fill in the form with the user’s credentials and submit it.
    before do
      fill_in ‘Email’, :with => user.email
      fill_in ‘Password’, :with => ‘password’
      click ‘Submit’
    end

    it ‘has a sign out link’ do
      page.should have_xpath(‘//a’, :text => ‘Sign Out’)
    end

    it ‘knows who I am’ do
      page.should have_content(“Welcome, #{user.email}!”)
    end

  end

  context ‘with invalid credentials’ do

    #No form entry should produce an error
    before do
      click ‘Submit’
    end

    it ‘has errors’ do
      page.should have_xpath(“//div[@id=‘errorExplanation’]”)
    end

  end

end

That is a trivial example and you would typically want to make more numerous and comprehensive asserts than that, but it should get you going with your first test.

Now that you have a modern integration test setup, I expect to see the quality of your app improve dramatically! Happy TDD’ing!

Comments [1]

Running a Static Site For Free on Heroku

Every now and then we need to run a static site on the cheap. This is easily doable these days, assuming you don’t expect a whole lot of traffic. One easy way to do it is with Heroku, since they give you a very small server instance for free with paid options to upgrade it in the future if you need to. For the purpose of our small static site, the free one will be just fine though.

Step 1: Create a folder for your site. It doesn’t matter what you call it. For purposes of this post, we will call it myapp.

Step 2: Create a Sinatra app to wrap it. Don’t worry, this is really easy to do even if you don’t know Ruby. We just need to create two files, and you probably won’t even have to modify them.

The first file will be called config.ru. Create this file in your myapp folder and put this inside it:

require 'app'
run Sinatra::Application

The second file will be called app.rb. Create this file in your myapp folder with this content:

require 'rubygems'
require 'sinatra'

get '/' do
redirect '/index.html'
end

Step 3: Copy all your static content in. Create a folder under myapp called ‘public’. Inside here, you can just copy in your whole static site. Just make sure that the root path goes to a file called index.html. If you used a different name, then change the redirect line in the app.rb to reference your root file.

Step 4: Create an account with Heroku. Now that the application is all set up, we need to create an account with Heroku and push the app out there. Go to http://www.heroku.com and sign up for an account right now. Once you click the confirmation link in the email, they will provide you with instructions to get you through the rest. You can either follow their instructions or mine. Either way they are largely the same. I’ll wait here while you get your account ready...

...OK, great. Now that you have an account you can create your application. Open up Terminal and cd to your myapp folder. From there, run the following commands:

Create your local git repo:
git init

Add all your stuff to it:
git add .
git commit -m ‘Initial commit’

Install the heroku gem so you can create your server:
gem install heroku

Create your application on Heroku:
heroku create

Push your application to the server:
git push heroku master

View your site:
heroku open

Now you should be looking at your static site in all its glory. You can go to heroku.com and edit the settings for your application if you want to add any premium features or put a custom domain on it. You can also change the subdomain of the app so you can more easily remember it.

Resources for this post:

http://www.sinatrarb.com/
http://www.heroku.com/

Comments [0]

Easy W3C Validation with Rspec and Webrat

Ever wanted to perform W3C validations on your screens right in your test suite? With the w3c_validators gem (http://code.dunae.ca/w3c_validators/) it is super easy!

First up, you need to install the gem and configure it with your test suite.

gem install w3c_validators
config.gem 'w3c_validators'

Now add a helper method to rspec so we can easily call the validator. Make sure you are including the support directory in your spec_helper.rb.

Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}

Then add a file that contains the helper method. With the below implementation, you can just pass in any path like you would a regular call to "visit" and it will perform the validation for you.

spec/support/w3c_validator.rb


include W3CValidators

def validate(url)
  visit url
  result = MarkupValidator.new.validate_text(response.body)
  result.errors.should == []
end

The reason it checks errors against an empty array is so rspec will print out the contents of the errors array when it fails. You can adjust the "should" to match how you want to see the errors in the log.

With all that set up, you can now call the validator in your tests


describe 'the home page' do
  it 'has valid markup' do
    validate root_path
  end
end

Being such a simple implementation it is pretty rough around the edges, but it gets the job done!

Comments [0]

Homebrew Installed MySQL Autostart

I really like homebrew (http://github.com/mxcl/homebrew/). It is a much more sensible alternative to MacPorts for package management in OS X. One thing that has been bugging me is that the launchctl for MySQL wasn't working. I followed the instructions from the install when it said to run the launchctl command, but MySQL would never autostart when I booted the machine. I rarely ever reboot so it isn't that big a deal, but still slightly annoying when I do.

Luckily, there is another easy way to launch it automatically. All you need to do is copy

$HOMEBREW_INSTALL/Cellar/mysql/{VERSION_NUMBER}/com.mysql.mysqld.plist

to

~/Library/LaunchAgents

...and you're done! I did it this morning and it is working like a champ. Just keep in mind that it will start mysql when you log in as opposed to when the machine actually starts. Not a big deal for development boxes though.

Comments [2]