My default testing stack:
- Minitest, with the
test 'something' do ...style
- No fixtures (or at least limit them as much as possible)
- spring to run tests quickly
- vim-test for running tests in vim (I’ve mapped
,.to run tests)
Some testing patterns that I follow
Write the assertions first, and then work backwards. For example, when writing tests for products#show, start by writing what you expect.
In the above test, we don’t have the variable
or even the
get :show call that calls the method.
but we can start filling in the details.
Now that we have filled in the details within the test,
we still don’t have the
which we can add at the top of the test.
Use bang version of AR::Base#create in the tests. In case where validations fails, this immediately raises an exception, rather than letting invalid data to pass through.
In the above example, if the Product validation were to fail for some reason,
Product#create would return an unpersisted instance of
As a result, we would get a routing error for products#show with id=nil,
which tells us nothing about the actual cause of failure.
Take the case when you were to add a new validation to Product.
Suddenly you see a lot of tests failing with routing error.
It would make things a lot easier if you could know
what vaildation is causing the failure.
Instead, if we’re using
#create!, we get an exception at that line,
and we know immediately what to fix.
Use Enumerable#fetch. Same reason as above - this causes immediate exception rather than propagating nils through the test.
Leave a failing/pending test when you take a break. This is great advice from Kent Beck’s excellent TDD By Example. It gives you a starting point when you come back to the code, rather than having to think about what to do next.
Start with a failing test.
Add something like
that will always fail
if you are writing tests for existing code.
Then, after seeing the failing test,
you can remove that line and write the proper test.
Avoid instance variables in
Instance variables do not raise an exception
when you have a typo.
In the below example,
if you typed
@title inside the test,
that would be nil,
post.update always sets title to nil,
this test would still pass.
Hash#dig - use
- High Low Testing
- Thoughtbot: How We Test Rails Applications
- Five Tips for Testing Rails
- A Guide for Writing Maintainable Rails Tests
- 7 Reasons I’m Sticking With Minitest and Fixtures in Rails
- Betterspecs - rspec guidelines with ruby
Fixtures, factories, etc.
- Mystery Guest - some good arguments on avoiding fixtures
- Rails Testing Antipatterns: Fixtures and Factories
- Tricks and Tips for using Fixtures effectively in Rails
- Getting Friendly with Fixtures
- Golden Master Testing (Katrina Owen) - Refactor complicated views
- Testing Client Side Views in Rails Apps
- Writing Deterministic and Performant Specs with Capybara