Notes from Effective Testing with RSpec 3, chapter 6.
As we learned from previous chapters, Integration Specs test code that depends on an api or code that cannot change. I’ve been talking about how Effective Testing categorizes specs differently than I do. Here we see the model layer being considered an Integration Spec because it has a SQL dependency. We test the Ledger class’s capabilities to interact with the database.
I’m starting to see the lines between test categories. In a Rails context, our models are unit tested because we’re testing the class itself; are the validations working, can it handle currency in different formats, does the behavior change depending on state, etc. We do not test the capabilities to store data because it’s a responsiblity of the Rails Framework. Testing behavior that a language or framework provides is usually unneccessary and will bloat the test suite.
Effective Testing is saying Ledgers dependency on SQL is covered by an Integration spec. While Ledger’s behavior is tested in an Acceptence spec.
Martin Fowler has recently written about Integration Specs. It provides an in depth explanation of different ways people think of Integration Specs.
Running specs in random order unveils problems that we haven’t thought of. RSpec provides a way to repeat the random order by providing a seed which you can pass back rspec –seed 32043. RSpec provides us another handy trick, bisect. We can set the seed flag, pass –bisect and RSpec will isolate the required tests and their order to reproduce the issue.
A cost of bloated test suites is the time and attention it takes to read through specs and understand an issue when they pop up. Using bisect we can cut out the fluff and just get to the essentials of understanding the problem. This will help reduce the cognitive load of shifting through specs.
We’ve been using metatags to filter examples that are run. Chapter 6 demonstrates another trick. We can use metatags to run configuration steps on specific examples.
In our spec_helper we can define the following config:
config.when_first_matching_example_defined(:db) do require_relative 'support/db' end
Then any spec can include :db to run require and run the code in ‘support/db’.
RSpec.describe 'Expense Tracker API', :db do
This is very handy. In the past I have needed to setup a mock web request for 30% of the test suite. I ended up putting it in a before hook in the config because it was faster than updating 30% of the examples. Using metatags is a cleaner solution: you only run the code that you need and the example declares it’s dependencies instead of hiding them.