A Trial-and-Error (or testing failures?) Adventure with Capybara and RSpec
After reading a bit about Test Driven Development (TDD) and Capybara, and using Test Driven Development for many of the lessons I have done while learning how to code, I realized that while Test Driven Development might not always suit every project, learning how to write tests (both the how and the why) would be a great skill. Whether building out my own projects, or someday in the future perhaps adding functionality into a new codebase and working with other developers, I know at some point I’m going to be expected to be able to write tests and not just rely on tests that are already built in, or solely using error driven development.
I decided to try out writing some Capybara tests of my own, from scratch, in relation to a small ruby web app, just to get a feel for the process, from start to finish, and document as I go, so thatI have a small reference guide for myself for the logic and the methodology.
What is the difference between Capybara and RSpec?
RSpec tests are generally used for testing your controllers and models. For a model, an RSpec test might check your validations, both of when an instance is or isn’t valid. RSpec tests are also used for controller tests, but writing out controller tests can sometimes get redundant. One benefit of Capybara tests is that it can check both the controller and the view almost bundled together, under the category of “feature.” Essentially, Capybara checks a page to see 1) that the information is displayed there and 2) how it is displayed. If your controller isn’t working properly, you won’t be getting your view pages, and thus your Capybara tests aren’t going to pass either.
Setting up the blog app
First things first, I’m going to make a blog app about dinosaurs completely from scratch.
I used the Learn.co guide on Rails testing as reference, and am using some basic commands to generate my directory and file structure:
$ rails new dinosaur-blog -T
Then, per the instructions, I want to add gem ‘rspec/rails’ and ‘capybara’ to my Gemfile, and finish up by generating a spec folder.
bundle install
bundle exec rails g rspec:install
I also have to require ‘capybara/rails’ in rails_helper.rb and integrate Capybara and RSpec in spec_helper.rb using require ‘capybara/rspec.’
Brainstorming my objectives (a “pre-test”, if you will)
Now, since my set up is pretty much ready to go, the basis of using Test Driven Development is to come up with tests before actually writing any of my code. This is something new to me, but since it’s a pretty basic web app, I figured I could come up with some ideas of the functionality I want ahead of time. This actually makes a lot of sense, because how can I really build something without knowing what I’m trying to build?
Objectives
I’m going to think through everything for my app, although I know that writing all of these tests would be a combination of both RSpec and Capybara tests. For this blog post, I’m only going to walk through one sample Capybara test, but I know that having a strong foundation of logic will help me in the future as I’m learning to write other tests. So. What do I need to start with while building this app?
Objectives
- Have a landing page that is an index with a list of the name of each dinosaur in my database
- Each of those names is a link to an individual page
- On each dino page, I can see: a picture of the dinosaur, in a header, its name and the period during which it lived, and a fun fact
- I can add a new dinosaur by clicking a button on the bottom of the index page, which brings me to a form where I add in each criteria (all of them are required)
- I can edit a dinosaur by clicking a link on the bottom of the dinosaur’s individual page, which brings me back to the form that I can then update
Okay, this seems like a good place to start. Number 3 seems like a good fit for a Capybara tests, because I know that it’s going to check to make sure the view is rendering, that I can see certain (dynamic) information on the page, and that the information in formatted in a certain way.
Now, although I have seen a lot of Capybara tests, I don’t really have any idea how to write them from scratch. So, I pulled up the RubyDocs for Capybara to start exploring the DSL.
Capybara DSL
There is a lot of very, very long documentation for Capybara. The main takeaways that I got were:
- Capybara tests are case sensitive!
- The documentation has information that relates to both classes and methods, which you can see listed out on the sidebar if you are looking for something specific.
- Capybara tests fall into one of the following categories in terms of what is being tested: navigation, clicking links and buttons, interacting with forms, querying, finding, scoping, working with windows, scripting (in drivers that support JavaScript — not relevant here), modals, and debugging. You can also write tests using sessions.
Writing a Capybara Features Test
Now, I’m going to get my sample test going. Remember that I have already added
gem 'capybara'
to my Gemfile. Then, I added a features folder with a dino_specs.rb file under my specs folder.
Finally, I started writing some sample navigation tests, again based on those I had already seen from Learn.co. Here is the example that I looked at for reference.
My objective here was:
- On the dinosaur’s individual page, I want to see in a header, its name and the period during which it lived, and in a sentence, a fun fact about it
require 'rails_helper'describe 'navigate' do
before do
@dinosaur = Dinosaur.create(name: "My First Dino", period: "When I lived", fact: "Dinosaurs are really cool")
endit 'shows the name on the show page in a h1 tag' do
visit "/@dinosaur.id">dinosaurs/#{@dinosaur.id}"
expect(page).to have_css("h1", text: "My First Dino")
endit 'shows the period on the show page in a h2 tag' do
visit "/@dinosaur.id">dinosaurs/#{@dinosaur.id}"
expect(page).to have_css("h2", text: "When I lived")
endit 'shows a fun fact on the show page in a p tag' do
visit "/@dinosaur.id">dinosaurs/#{@dinosaur.id}"
expect(page).to have_css("p", text: "Dinosaurs are really cool")
end
end
Then, I ran
bundle exec rspec spec/features/dino_spec.rb
And, moment of truth…
Here are the failures:
I have never been happier to see failing Capybara tests!!!!!!!
Each of them are related to navigation and note that I don’t have my routes set up! This is great — my test is working! Obviously this is just the first step of many, and as I’m building my code, the failures or errors will change.
Take Aways
The logistics of writing tests with Capybara is fairly simple, once you have the correct folder structure and gems installed. That said, I wrote one of the most simple tests that I could, just to see if I could get things working. I’m sure it will become more complicated as I try to write more complex and interdependent tests.
As for using the DSL, what was most helpful for me was looking at models that are similar to what I’m creating and using them as reference until I get more familiar with the syntax and best practices for Capybara.
Knowing why you’re testing and how to make flexible but useful tests really seems to be something you just have to learn by doing. I read lots of blogs and resources, and basically all of them said, you can try to learn it by reading references or the docs, but really the best thing to do is learn by reading tests, practicing writing tests, and watching more experienced programmers as they develop their tests. Capybara is really just another kind of programming, and a lot of developers consider that when you’re writing tests, the code is just as important as the code that builds your program.
What’s Next
During the writing of my Capybara tests, I also explored writing RSpec tests, as mentioned earlier in the post. I found this great resource, RSpec Testing for Beginners, Part 1 by Ed Wasserman, which gives a great setup for building out a quick RSpect setup using rails with a dummy model. I did have trouble translating this information into something I would be able to integrate into my own programs, so this will definitely take a bit more for for me to figure out and start using on my own, but I think it would be a great skill to have.