Dynamic context in Rspec - don't repeat yourself
03 Nov 2010
We used to have less problems with subject and behavior when writing our unit tests. And context is what testing is all about.
Web apps manage data and the same method can return different values based on current database state.
Advanced rspec features make it possible to use very effective technique to organize tests.
In order to reflect your non-linear business process in unit tests you can not only nest contexts in each other but also make your context more dynamic.
Context callback
Rspec#let
method is used to lazy load things into context.
In spite of lazy initialization, let blocks are defined before the before each
hook.
This allow us to create context callback.
Understand by example. In this example orders should be delivered to confirmed customer account just after creation and should not be delivered if the account is not confirmed yet.
describe Order do
context "after create" do #defining a partial context
before(:each) do
subject.customer.confirmed = _confirmed
subject.save!
end
context "when customer is confirmed" do
let(:_confirmed) { true }
it { should be_delivered }
end
context "when customer is not confirmed" do
let(:_confirmed) { false }
it { should_not be_delivered }
end
end
end
Here you can see partial context definition and custom behavior in two nested contexts.
I used underscore prefix in order to identify the callback method, that is defined differently in different contexts.This happened because
let
as method is defined at once but before
block is called only when it
is reached.
Testing utility function
Another example that is a kind of pattern matching(Erlang term), designed to test utility functions.Suppose we have a boolean expression evaluation function:
describe Expression
describe ".run" do
subject { Expression.run(arg) }
context "with '&' where both true" do
let(:arg) { "true & true" }
it {should be_true}
end
context "with '&' where one false" do
let(:arg) { "false & true" }
it {should be_false}
end
........
end
end
Very good strategy to run same function with different arguments.