Rails 5 Test Prescriptions: Build a Healthy Codebase - reading notes
Mar 27, 2019 · 4 minute read · Commentsrubyruby-on-railstesting
I finished reading Rails 5 Test Prescriptions: Build a Healthy Codebase - reading notes by Noel Rappin. It contains exactly what it claims in its lengthy title. It discusses testing in more general terms (but doesn’t go deep into motivation for testing as many books do), describes different categories of tests and puts them into practice with Rails 5 approach.
Compared to other books, this is actually pretty hands-on text. Rappin uses a simple project management app as an example to demonstrate the various approaches throughout the book. It is very much slanted towards the Rails approach but contains critical comments about the path chosen by the Rails team (and especially DHH). Personally, I found the sections on differences between Rails 4 and 5 approaches and the differing naming conventions the most valuable.
However, the whole book is packed with useful comments and tips, tool options you didn’t know about at all (or enough), classification for problems that get muddle way too easily etc. My reading notes follow:
- you can run faster tests by separating test config into multiple files and loading only the necessary code, e.g. plain Ruby vs. Ruby on Rails
- RSpec has
have_attributes
matcher, useful for anything from ActiveRecord objects toStruct
s - custom matchers are automatically generated for boolean attributes of ActiveRecord, e.g.
expect(user).to be_admin
- RSpec offers
specify
, an alias forit
, to make tests more readable whenit
doesn’t fit - RSpec can aggregate failure from multiple expectations within a spec (instead of stopping at the first one)
via
aggreate_failures
- I wouldn’t turn it on by default but it’s useful when debugging - if you use fixtures, they are converted to SQL
INSERT
s directly, no model callbacks or validation gets called - FactoryBot has
attributes_for
which returns a Hash of attributes instead of an instance of a model; useful when you need only the attributes, e.g. when creating an HTTP request body - FactoryBot functions
create
,build
,build_stubbed
take an optional block as an argument and yield the new instance to it - useful when you need to call some method on the model or pass it to some other objects as part of the test setup - FactoryBot’s
build_stubbed
provides a fake ID for the object so it can participate inbelongs_to
associations (which require an ID) without actually being saved - clever but can bite you - FactoryBot factory associations are created by the same method as the entrypoint factory, e.g. if you
build_stubbed
a project, itbuild_stubbed
s a task; however, if youbuild
the entrypoint factory, itcreate
s the related object because just building it wouldn’t yield an ID - this can yield to unnecessaryINSERT
s - it’s generally best not to define associations in a factory - if they are needed for a test, they should appear in the test anyway, if they are not needed they would only slow down the tests; this is the ideal but in goes against foreign key constraints which often require large dependency trees
- time-like classes accept
to_s(:db)
which produces a unified representation useful for comparison across types - if you define a mock via
allow(game).to receive(:score) { 5 }
you can then turn it into a spy byexpect(game).to have_received(:score)
- it’s functionally equivalent to usingexpect
straight away but allows for setup-run-verify structure of a test spy(name)
==double(name).as_null_object
instance_double
,class_double
andobject_double
create appropriate doubles but also verify that the received messages exist on the doubled object ensuring that the test and the implementation have the same interface (at least the relevant part) - a very valuable band-aidverify_partial_doubles
turns on the same verification for partial doubles, it is turned on by default in new project but the default is off to provide backwards compatibility, so go check it- ActiveJob doesn’t run jobs in feature specs by default, you can force it via
perform_enqueued_jobs
- controller specs are no longer a thing in Rails 5, the functionality still lives in a separate gem
- ActiveJob has a special matcher
to_have_been_enqueued
- testing the contents of a multipart e-mail is a bit tricky
save_and_open_page
let’s you get a snapshot of the application at any point in a feature spec- RSpec has an extremely useful option
--bisect
which allows you to find the lowest number of tests you need to reproduce a failure which is dependent on the order of the tests; this happens when tests affect global state, e.g. god objects or a database (autogenerated primary keys) - it is useful but expect it to take long, the longest run I’ve encountered was almost 7 hours
These are my takeaways from the book. I believe any developer will find something useful in there.
We're looking for developers to help us save energy
If you're interested in what we do and you would like to help us save energy, drop us a line at jobs@enectiva.cz.