We'll answer the following questions in this talk.
- Why should you care about testing?
- How do you know what to test?
- What is the purpose of a feature test?
- What is the purpose of a unit test?
Tests limit what we have to debug. For instance, if we have all green, passing tests, we know that when we deploy to Heroku our code isn't the problem, rather an issue with Heroku. Passing tests limits the types of debugging you have to do.
By the end of this lesson, students should be able to:
- Develop a Rails API using outside-in, behavior-driven testing.
- Make user stories to drive wireframes.
- Drive behavior specification with user stories.
- Write automated CRUD request specs with RSpec.
- Drive routing, model, and controller specs using request specs.
- Fork and clone this repository.
- Install dependencies with
bundle install
. - Run
bundle exec rake db:create
andbundle exec rake db:migrate
.
Before diving into testing, let's revisit wireframes and user stories. We've used both of these for our first projects.
Why were they important? What do our user stories do for the scope of our project? How are they useful when wireframing our webapp's layout and UX? How do good user stories and wireframes help with app development?
Feature tests are for catching regressions/bugs. Features break less because they're higher level. Features test user experience. Feature tests document workflow within the app. Feature tests tell you what's missing, and drive each step of the development process.
Unit tests drive implementation and break more often, but they're smaller in scale and faster to execute. Unit tests test developer experience. Unit tests don't break down the problem into smaller pieces, they give you the confidence that the smallest pieces work as expected. Unit tests document your code base.
Both of these tests provide documentation of your code. Writing tests makes refactoring easy because we can change one thing and see how it affects the entire system. After each change in the code we run our unit tests to confirm our expectations.
Use feature tests to drive your unit tests, and your unit tests to drive your code. You'll want to save and commit your work often during development. We suggest commiting:
- After passing unit tests.
- After passing a feature.
- After refactoring and passing all tests.
You can push when you're done passing a feature. You should always run your tests before you commit/push your work.
We'll start with a request spec. Request specs perform a similar job to curl
:
they emulate testing your API from the client's point-of-view.
Failing request specs will drive creating routing specs. Routing specs will drive creating controller specs. Finally, controller specs will drive creating model specs. Once we have all these smaller tests (units) passing, the feature spec (request spec) should pass automatically!
Let the tests tell you what to do next, and you'll never have to think about your next task. It helps us get in "the zone"!
To check our specs, we run bundle exec rspec spec
from the command line.
What output to we get?
Failures:
1) Articles API GET /articles lists all articles
Failure/Error: get '/articles'
ActionController::RoutingError:
No route matches [GET] "/articles"
# ./spec/requests/articles_spec.rb:29:in `block (3 levels) in <top (required)>'
This output tells us exactly what went wrong (or more accurately, what did not go as expected), and should be treated as our guide towards working code.
Let's work on our GET /articles
routing spec in spec/routing/articles_spec.rb together
to ensure that our routes are mapped to the correct controller method.
To wrap up our checks that all articles are correctly returned from our index
method, we'll need a passing test for the controller method itself: spec/controllers/articles_spec.rb.
In spec/requests/articles_spec.rb, let's make sure our API is returning a single article correctly.
How do we make sure our routes are set to receive GET requests for a single
article? How does routing to articles#show
differ from articles#index
?
Working off of our articles#index
, build out the two GET show
tests in
spec/controllers/articles_spec.rb to
pass. Again, remember how articles#show
differs from articles#index
and
be sure to be testing against that.
Based on our GET
specs, complete request
and routing specs for POST
, PATCH
, and
DELETE
.
Continue working in spec/controllers/articles_spec.rb to
create passing tests for the POST
, PATCH
, and DELETE
controller actions.
In spec/models/articles_spec.rb, we will need to write tests to check for the following:
- Articles can have many comments.
- If an article is destroyed, its associated comments must also be destroyed.
Based on our Article
Model specs, run your specs to complete what is expected
at app/models/article.rb.
Run one spec at a time until they have all passed.
In spec/models/article_spec.rb, let's test to see if we:
- are associating comments to articles
- have set our
inverse_of
record - are deleting comments associated to articles when articles are deleted
Using our BDD skills, let's create tests to check that our Article model is
validating the presence
of content
and title
. We don't want articles created that are missing
either one of those fields.
We will create our tests first and let those drive us towards an adequately-validated model.
Create and run through request, routing, controller and model specs for our Comments resource.
- RSpec Rails on Github
- How I learned to test my Rails Applications, series
- Better Spec
- Rspec Docs from Relish
- A great example of outside-in testing from Ruby Tapas
- #275 How I Test - RailsCasts
- How We Test Rails Applications
- All content is licensed under a CCBYNCSA 4.0 license.
- All software code is licensed under GNU GPLv3. For commercial use or alternative licensing, please contact [email protected].