Setting up Rails testing with rspec, devise, and the gang
so much fun
- tags
- rails
- happy_seed
- testing
- ruby
Contents
The goal is to get features out fast, and iterate on them quickly. Does anyone care about it? What do they care about? How do we make it better?
As projects get bigger, both in terms of people using the site as well as people working on the site, testing and quality become relatively more important. Adding tests introduces drag, and the theory is that you invest now for payoffs later. What’s the least amount of drag we can add to the process to get us in a good place for when it will start to pay off?
RSpec feature specs lets developers create testing click paths through the application, looking at the flow though the app. It doesn’t focus enough on the design details, and going through a test driving development process with this way will end up with sites ‘working’ using only an engineer’s defination, so as a MVP implementor you’ll need to find a way to resist the temptation, no matter how nifty the backend flow state is.
This article will go through how the testing environment is set up on HappySeed where we focus mainly on RSpec feature specs. Seed does this all automatically, but its nice to have it written down in a one place so we all know what’s going on. Let’s get into it.
Install RSpec, FactoryGirl, Capybara, and Guard
Lets fire up a new rails app, and pass the -T
option to not include TestUnit.
|
|
Now add some gems to the Gemfile
.
|
|
Lets go through this:
rpsec
(implied as a dependancy) is the testing framework that we are going to usefactory_girl
(implied) lets us code smart database fixtures in ruby, in things called factoriescapybara
lets helps you test web applications by simulating how a real user would interact with your app.guard
(implied) makes rerunning tests a snap, by watching the filesystem for when you save files and triggering events automaticallywebmock
locks down your test environment from talking to the internetvcr
record your test suite’s HTTP interactions and replay them during future test runs for fast, deterministic, accurate testsrspec-rails
is the rails integration with rspec, which depends upon rspec itself.factory_girl_rails
is the rails integration with factory_girl.guard-rspec
is rspec integration with guardspring-commands-rspec
lets you integrate rspec with spring, which means that your tests will run much faster.
If you are interested in using cucumber, see below for steps to integrate it.
Base configuration of rspec and guard
Lets make sure that spring knows about rspec:
|
|
This creates a bin/rspec
command that lets us run our tests. Lets create a Guardfile and then tell guard to use this command:
|
|
Then edit the newly generated Guardfile
to use the spring version of the rspec runner. I also like to run all of the tests when I start up guard, because the more the merrier. In Guardfile
, change the guard :rspec
line to:
|
|
Now we install the rspec files:
|
|
This creates 3 files:
|
|
I like to have the output in documentation format by default, so we can edit the .rspec
file to be:
|
|
Add login support and factory girl to spec_helper
Getting feature and controller specs to work with authentication is a bit tricky. We’re going to assume that you use devise for authentication, but this should support anything that uses warden:
Add devise to Gemfile
:
|
|
Create spec/support/controller_helpers.rb
|
|
At the top of spec_helper.rb
add:
|
|
and in the config block add:
|
|
Then edit rails_helper.rb
and put the following in the config block:
|
|
If you are getting uncaught throw :warden errors then make sure you have the right includes in the right config files!
This gives you a few things:
FactoryGirl
syntax methods, which let you use things likecreate( :user )
in your tests, which usesFactoryGirl
to generate a database object.- helper methods to
sign_in
a user for a specific scope. This could be used assign_in create( :user )
at the beginning of your controller test methods, andsign_in nil
to simulate an anonymous user.
Example controller spec
This example assumes that you’ve already setup devise. If not, you can get something up and running doing:
$ rails g devise:install && rails g devise User && rake db:migrate
(I go into slightly more detail in setting up devise for authentication.)
Assuming that you’ve already setup devise, lets look at how to implement a controller spec. First lets create a simple scaffold for a post:
|
|
And lets make it require authentication in posts_controller.rb
:
|
|
And replace posts_controller_spec.rb
with:
|
|
When we run guard
, a few of the scaffold generate things will fail, since we are now forcing signin to check out this page. Our spec will correctly redirect the user to sign in.
Lets now simulate a logged in user:
|
|
Guard should try and run this tests but fail because we can’t configured a user factory.
Lets set that up now in spec/factories/user.rb
:
|
|
Going back to the guard window, press return to run everything again and verify. (Guard doens’t know that the posts_controller_spec.rb
depends upon the user factory changing.)
Example rspec feature
Feature specs are more interesting, since they let you walk through the site and how it works. Lets build one now:
|
|
And lets add something there now:
|
|
This first creates a new user in the database, and then
- Visits the
new_post_path
url. - We should be shown the login form, and we fill it out
- Click the log in button
- Fill out the new post form
- Create the post
- Make sure that it’s in the database
If you want to skip the steps of manually logging in the user, you can use the login_as
method like so:
|
|
Setting up webmock and VCR
Webmock stops requests from hitting the network in the test environment. Lets turn this on by editing rails_helper.rb
:
In rails_helper.rb
, below require 'rails/rspec'
:
|
|
And VCR lets you record and play back tests in the test environment, so the first time it’s called it lets it get through, and then on subsequent requests it plays back a known response. Put this at the bottom of rails_helper.rb
to configure:
|
|
Example usage of webmock and VCR
Word comes down from up high that to facilitate decision making, a key bit of information is the current phase of the moon when something is posted to the site. We can pull the data from the Cerridwen site, but how do we set up tests for this? We certainly don’t want our test suite to fail because the phase of the moon changed!
This is where webmock and VCR comes in. Webmock by itself will throw an error when a request is made over the network. VCR will let us record and play back network request – using cassettes no less – so that we isolate changing API responses from our tests.
First lets write our code to look up the phase of the moon on the create post action. Let’s add a field
|
|
Then add the lookup to our app/controllers/posts_controller.rb
and replace the def create
method:
|
|
(This is not good style.)
Now lets run our test again.
Lots of unhandled http request errors! I’m going to ignore the scaffold generate tests and focus on our rspec feature. So, inside of spec/features/add_new_posts_spec.rb
we can link the click action inside of a use_cassette block like so::
|
|
And let the tests run. When tests are run, VCR is going to look for the file spec/vcr/waxing_in_pisces
. If it’s not found, then it will make the request to the server. If the API requires authentication, for example an accesstoken when accessing a OAuth protected call, then you’ll need to configure you tests to have valid credentials for the first time. These credentials could be written into the spec/vcr directory, so make sure that you scrub that before things get checked in. (Or expire the access tokens.)
If it is found, then it plays it back and so the next time the suite is run it plays it back. Which is way faster.
Final notes
Testing has a place in software development, but it’s not actually clear where that is. Setting up test haresses and building out tests for every feature really slows things down in the beginning, and probably isn’t appropriate for one off, exploratory things. On the other hand, things tend to grow way past what anyone ever initially expected, and you always say “I wish there was a test suite” when inheriting code – or even more likely, looking at something you haven’t seen for a few months.
The rails community has a long history with Test Driven Development. The biggest advantage of this is that you write code that is testable, and code that is testable has all sorts of other qualities that is great, from clearly defined boundries of responsibility, good encapulation, and being able to look at small isolated pieces of the system and having it make sense. On the other hand, thebest code is the stuff you don’t need to maintain, and in some projects the weight of the test code is as large or larger than the code for the application features.
The biggest problem with testing is that it gets you in a wonderful, but wrong, flow state of development. In the begining of projects you should be focused on the user experience, and how the system is experienced from the outside. The highest level tests tend to be are user stories, which is below the level of brand and user experience.
Previously
Next