Notes from Effective Testing with RSpec 3, chapter 2.
Chapter two walked us through creating a spec to guide the development of our class. This time we explored the useful ways RSpec runs tests. We learned about structuring specs with tags and context blocks. Then filtering what specs are run.
We can assign meta data to an example block using hash value syntax. Rspec can then filter these tests to only run with or without them.
In ‘Identifying Slow Examples’ we used profile to identify the last two examples as our slowest tests. We can mark these tests as ‘slow’ using the code below:
RSpec.describe 'The sleep() method' do it ('can sleep for 0.1 second') { sleep 0.1 } it ('can sleep for 0.2 second') { sleep 0.2 } it ('can sleep for 0.3 second') { sleep 0.3 } it 'can sleep for 0.4 second', slow: true do; sleep 0.4; end it 'can sleep for 0.5 second', slow: true do; sleep 0.5; end end
We can then run just the ‘slow’ tests, or exclude them using command line options or configurations:
# exclude slow tests from our run rspec --tag ~slow # only run the slow tests rspec --tag slow # Configure Rspec to auto exclude tests marked as slow RSpec.configure do |config| config.filter_run_excluding slow: true end
Effective Testing walks us through configuring RSpec with `filter_run_when_matching`, but there are many filters we can use to configure RSpec.
In addition to slow, we can use meta-tags to filter
- Required: Features that have contractual obligation to be available. You may never use TDD to ensure a Partners link is available on your front page. You may want a set of tests for ease-of-mind on your business relationships.
- Components & Stories: Tests that cover a general idea or the next milestone.
- Smoke tests: quick check to ensure application is functional.
Another way of filtering specs is with pending. Pending is way of categorizing specs that are not yet ready. It allows us to sit down and completely think out the attributes of a class.
RSpec.describe 'blog post' do it 'has a title' it 'has a subtitle' it 'has content' end
This is easy to knockout and easy to read. I don’t have to know how I am going to accomplish this behavior but get’s things out of my head. Making me less likely to forget the big picture.
Pending goes further into detail. We can mark a test with pending and provide a helpful message.
it 'is light in color' do pending 'Color not implemented yet' expect(coffee.color).to be(:light) end #Rspec output 1) A cup of coffee with milk is light in color # Color not implemented yet Failure/Error: expect(coffee.color).to be(:light)” #Rspec output once completed 1) A cup of coffee with milk is light in color FIXED Expected pending ’Color not implemented yet’ to fail. No error was raised. # ./spec/coffee_spec.rb:42” # Excerpt From: Myron Marston, Ian Dees. “Effective Testing with RSpec 3.” iBooks.
If we forget to remove pending after completing a feature, RSpec will remind us to clean up our test. The authors point out that this is handy for bugs. An unexpected failing test can be marked as ‘pending’ with the issue tracker in the description.
In ‘Running Just What You Need’ we learn about the −−example or -e flag. This is exciting because combined with −−dry-run we can easily explore our specs as documentation. In the last chapter, the authors pointed out that let safeguards us from memoization gotchas. Curios about what let is expected to do I can quickly find out.
rspec --example let --dry-run ... #let raises an error when referenced from `before(:all)` yields the example raises a useful error when called without a block caches a nil value caches the value raises an error when attempting to define a reserved method name generates an instance method raises an error when referenced from `after(:all)` does not pass the block up the ancestor chain when the declaration uses `return` can get past a conditional `return` statement can exit the let declaration early when overriding let in a nested context can use `super` to reference the parent context value when included modules have hooks that define memoized helpers allows memoized helpers to override methods in previously included modules ...
47 examples are returned. This is more than we wanted, but it’s much easier to scan 47 examples instead of thousands. It’s also easier to read as it is just the authors intentions. There are no stack traces, profiling, or meta-data.