Rails Testing Crash-Course: Notes
Check out this repo at https://github.com/danielchangNYC/YATL
Setup
Changes are already included in master, but if you want to reference the steps, check out testing_setup.md or the second commit.
Setting up Data for tests
- Fixtures
- YAML file for preset, hard-coded sample data you can call in your tests
- PROS: Fast
- CONS:
- Can get unwieldy as it grows and there's a need for edge-cases
- Bypasses actual creation and therefore validations
- FactoryGirl
- PROS:
- Easier to make edge-cases
- Easily modifiable
- Extremely readable
- Validates with creation
- CONS:
- Slow
- PROS:
- Mocks and Stubs: Saving this for later.
What does our development-cycle at Flatiron Labs look like?
- Your future job may have a codified style-guide and a preferred work-flow that's different, but you'll probably find a lot of similar principles guiding those choices
- TDD
- Write tests
- Watch them fail
- Pass them (with minimal code)
- Refactor
- BDD
- Involves business side in Agile development process
- "Is the feature good enough for the client to accept it into the system?"
- Uses TDD
- "Outside-in"
- Test from the perspective of the user
- How will someone use your feature?
- What will he/she expect to see and do?
- View (acceptance/feature/integration tests) -> Controller (functional tests) -> Model (unit tests)
- Test from the perspective of the user
- As opposed to "inside-out"
- model -> controller -> view
User Stories as an Agile Development tool
- User stories clarify what a feature does, who will use it, and why it's valuable to them.
- Helps us communicate more clearly with the client
- Format
- As a... (stakeholder)
- I need to... (feature)
- So that... (value-added)
- Examples
- As a user, I need to create a to-do so that I can keep track of my actions for the day.
- As a user, I need to mark a to-do as finished so that I can quickly view what else needs to get done.
Check out the Pivotal Tracker for this repo
If that doesn't work, here are a few user stories you can work on. We'll go through the first one together as an example.
- As a user, I need to create a problem so that I can hide my real problems.
- Solution: Branch Iteration1
- As a user, I need to delete problems so that I can pretend I forgot.
- Solution: Branch Iteration2
- As a user, I need to edit a problem so that I can turn a situation into whatever makes the most sense to my erratic heart at the time.
- Solution: Branch Iteration3
- As a user, I need to mark a problem as finished so that I can feel a fleeting sense of security before what's bound to happen later: more problems.
- As a user, I need a button to generate problems so that I can close the work-on-problem/create-a-problem ratio.
Cucumber Test (a type of Acceptance Test)
- Compare: Feature test (w/o cucumber) and Acceptance test (w cucumber)
- 3 A's
- Arrange
- Act
- Assert
- Cucumber turns the 3 A's into Given/When/Then statements that take
regex
arguments - All cucumber tests go in the
feature/
folder. You can create whatever subfolders you'd like. - All statements go in
feature/step-definitions
and are reusable. - run your cukes in command-line with the command
cucumber
Controller Tests (a type of Functional Test)
- What do we put in a controller test? => What is the purpose of a controller?
- Assign values (
@user = User.find(params[:id])
) - Save data to the database
- Render
- Redirect
- Assign values (
Model Tests (a type of Unit Test)
- Test one thing at a time
- Good: "it 'generates a Chat between two users'"
- Good: "it 'marks a lesson as finished'"
- Smelly: "it 'generates a token and associates a user to the token'"
- Don't over-test
- Don't test something that's already been tested (eg. Rails or Devise)
- Happy path / Sad Path
- Happy Path: it 'generates a Chat between two users' - Sad Path: it 'throws an error if a user does not exist'
Pair-Program / Try it yourself
Directions
- Make sure you've cloned the repo.
- Look at the user stories on the pivotal tracker for this application.
- Start the first card in the Current column and write your tests. Start with the Cucumber tests and then proceed inwards towards Model specs and back out.
Quick Review:
- Test one thing at a time
- Don't over-test
- Happy/Sad paths
- 3 A's
- TDD
- BDD
Better Specs
- cucumber tags
- Write
@tag
above anyScenario
s, then typecucumber -t @tag
to run any tagged cukes. - Write
@javascript
to run aScenario
using theselenium
web-driver.
- Write
- Use Mocks and Stubs. NOTE: This section is a work in progress.
- Stubs define the return value of a method.
scraper.stub(:scrape_page).with("<html>...</html>")
- OR:
scraper.stub(:scrape_page) {"<html>...</html>"}
- Use-case: You need to call a slow or taxing method, but it's not the main focus of your test.x - In the exampleou, we're testing a method returns the number of
<ul>
elements in a page that you scraped, which reqreous us to call#scrape_page
. Stub the return value of the#scrape_page
method with a hard-coded string of- TML.
- Use-case: You need to call a slow or taxing method, but it's not the main focus of your test.x - In the exampleou, we're testing a method returns the number of
- "Mocks define an expectation that a message will get sent." - Sandi Metz, POODR pg 217
scraper.should_receive(:scrape_page)
# => tests that the message gets sent but doesn't necessarily run the code in that method
- Used Together
allow(scraper).to receive(:scrape_page).and_return("<html>...</html>")
- Shorter:
allow(scraper).to receive(:scrape_page) { "<html>...</html>" }
- Stubs define the return value of a method.
- DRY: Use
let
andlet!
statements toArrange
data when possible; usebefore :each
blocks toAct
when possible
Recommended Resources
- Highly-Recommended Reading: Everyday Rails Testing with RSpec, by Aaron Sumner
- POODR (Programming Object-Oriented Design in Ruby), by Sandi Metz
- Difference between test types
- c2.com for more terminology stuff
- Acceptance Tests in the BDD world ("Is the feature good enough for the client to accept it into the system?")
- Integration tests? => Test the integration of several units into a larger system. Some people refer to feature specs as integration specs.