Testing basics [3.5d]
Automated tests are how we keep a low defect rate without a QA department. This lesson covers why we test, the difference between unit and end-to-end tests, and the basics of RSpec and Capybara.
Important
Work on this lesson in
teachermode.
Learning goals
- You can explain why we test: fewer defects without a QA department, freedom to change code, frequent deploys, no feature lost by accident.
- You can explain the difference between unit tests and end-to-end tests, with the strengths and costs of each, and why we rarely test controllers or views in isolation.
- You can decide which tests a change needs: an end-to-end test for the happy path, unit tests for the edge cases.
- You can write unit tests with RSpec, structured by the behavior under test.
- You can write end-to-end tests that drive the application through a browser, using RSpec feature specs with Capybara.
- You can run a whole suite or a single example, read a failure and find its cause.
- You can explain "test the behavior, not the implementation" and spot a test that is coupled to the implementation.
- You can name the costs of testing and argue against testing too much.
- You recognize the legacy
shouldsyntax in older specs.
Resources
Read what's new to you, skim what's familiar, skip what you already master. Stop when you can meet the learning goals.
Your agent can also generate an overview, a tutorial or an explanation for anything here, tailored to what you already know. Just ask.
One word on names: most sources today say system specs where we say feature specs. Both use the same Capybara DSL; we write type: :feature specs, and everything you read about system specs applies.
Why and what to test
- 📘 Chapter "The value of tests" from Growing Rails Applications in Practice Show archive.org snapshot (in our library)
- 📄 Write tests. Not too many. Mostly integration. Show archive.org snapshot — the reasoning behind our testing hourglass in six minutes: a few end-to-end tests for confidence, many cheap tests for details, no chasing coverage
- 📄 Pyramid or Crab? Find a testing strategy that fits Show archive.org snapshot — the different test-suite shapes side by side
- 📄 Guidelines for Testing Rails Applications Show archive.org snapshot — the same discussion for a Rails/RSpec suite
RSpec and Capybara
- 📘 Everyday Rails Testing with RSpec Show archive.org snapshot (in our library Show archive.org snapshot ) — step by step; chapters 1 (Introduction), 2 (Setting up RSpec), 3 (Model specs), 4 (DRY enough specs), 8 (Testing the user interface with system specs), 9 (More DRY testing techniques)
- 📄 RSpec documentation Show archive.org snapshot — reference; start with the built-in matchers Show archive.org snapshot and the feature spec example Show archive.org snapshot
- 📄
Capybara README: The DSL
Show archive.org snapshot
and the
Capybara cheat sheet
Show archive.org snapshot
—
visit,fill_in,click_on,within,have_content - 📄
Better Specs
Show archive.org snapshot
— how to structure specs; why
expect, notshould. Take itslet/one-expectation rules as guidance, not law - 📄 rspec-rails: system specs, feature specs, request specs — what's the difference? Show archive.org snapshot
Other formats
- ▶️ Drifting Ruby #102: Feature Testing with Capybara Show archive.org snapshot — 15 minutes; the DSL is unchanged, only the driver setup is dated
- 📘 Testing Rails Show archive.org snapshot — free PDF by thoughtbot; chapters "Why test?" and "Types of Tests"
- 📄 How we test Rails applications Show archive.org snapshot — thoughtbot's 15-minute overview of all spec types in a stack like ours
Your MovieDB already comes with working RSpec examples.
Exercises
Play with existing tests
Pick one of the sample apps.
- Run one unit spec file and one feature spec file. They should pass.
- Find an RSpec unit test and break an expectation. See what happens when you re-run the test.
- Find an RSpec feature test and break an expectation. See what happens when you re-run the test.
Your first E2E feature
- Write a feature test for CRUDing movies in your MovieDB application.
- Use Capybara to talk to the simulated browser.
Unit vs. Feature testing
Add the following feature to MovieDB:
- Above the movies list is a text field and a button "Search"
- When the user enters text into the text field and hits "Search", the movies index is filtered to only contain matching movies
Some hints for the implementation:
- Your
Moviemodel should gain a class methodMovie.search(query)or even better a scope method you can apply to the scope in the controller. Why should this not be an instance method? - You can implement the search in either SQL or Ruby
-
form_with model:is not helpful for the search form above the index. Why is that so? What can you use instead? - Add a custom route
/movies/searchto your routes, and map it to a new#searchaction inMoviesController - Try to reuse some of the existing private methods for the implementation of
#search - When rendering search results, try to reuse the existing index views, or at least large parts of its.
Now add specs for both the new method Movie.search() and the new UI.
Follow the advice from the "Testing" chapter from Growing Rails Applications in Practice Show archive.org snapshot :
- RSpec feature specs for the most common "happy path"
- RSpec unit tests for all the edge cases (like case insensitivity)
From now on
When doing coding exercises in future cards, always deliver tests together with your implementation.