Lately, I’ve decided to spend a lot of focus on writing feature specs with RSpec and Capybara. Feature specs allow you to test your application from the user’s point of view. You use the Capybara gem to test your application’s interface with commands like these:

(A point of confusion: sometimes these kinds of tests are called “acceptance tests” or “integration tests,” and now DHH is calling them system tests and is declaring that a future release of Rails will provide functionality for tests like these.)

I’ve found that this methodology tests 80% of what I need without needing to dive into the minutiae of testing every boundary case in every method in every class. Don’t get me wrong; I still test most custom logic in model specs and some authorization logic in request specs, but I try to not go overboard. A deeper level of detail in my testing can come later when my app becomes a wild success, amiright?

My understanding is that these tests run on the slower side compared to other types of tests that RSpec can run, but I’ve found the trade-off to be worth it to test the whole system from the user’s point of view.

Tutorial: let’s get into some CRUD

Simple CRUD functionality is a great place to start to get a feel for how this all works. Let’s do this for a simplified Product model in the admin area of a fictitious e-commerce application.

And quick configuration of some validations on the Product class at app/models/product.rb:

(I may as well use this example as a chance to pimp the wonderful money-rails gem for handling currency.)

Testing creation

A good place to start is a product creation area. We can go ahead and setup the route under an admin namespace:

Then we write our test first at spec/features/products/admin_creates_product_spec.rb. I enjoy the suggestion by Thoughtbot to name your features with an actor and then an action, thus naming the feature Admin creates product.

With this in place, you can build the functionality to make the test pass. The finished product would look something like the following files.





(Note that the code above uses the amazing Simple Form gem.)


We’d think that this code would make the test pass, but there is a problem:

We need to add a handler for our flash message to app/views/layouts/application.html.erb:

Then we should see the test pass:

Next, we can add a test that the form correctly reports validation errors when the user doesn’t enter data correctly:

And then update the create action to handle validation errors:

You should get a passing grade from RSpec:

To see the code committed up to this point, check out this commit and the tree at that commit on GitHub.

Handling updates and deletes

I assume that you know how to build basic CRUD, so I will leave that as an exercise for the reader. Or hey, you can just look at what I committed to the GitHub repo.

However, this post is about feature specs, so this is how I would write the specs for the update and destroy actions.


Notice that in this spec’s with valid input scenario, we’re using the form to replace the values persisted by product with the values from new_product.


Other miscellaneous feature spec examples

This is but a mere blog post, but I could demonstrate a few other features of Capybara that I use quite frequently in feature specs.

If you want to check for content within a specific block of your page, you can pass your expect assertion(s) in a block to within, using a CSS selector:

Testing email that your app sends and receives is simple with the email_spec gem:

You can also test JavaScript functionality using the selenium-webdriver gem and by adding js: true to your specs:

Want to learn more?

I found these to be good resources for fine-tuning my feature spec skills: