More than once, I’ve had to check an array for it’s order in RSpec. Usually, I just sort the array, and compare it to itself. The spec might look something like
describe MyObject describe "scope #ordered" do it "returns results in most recent order" expect(MyObject.ordered.all.sort(&:attr_to_order_by)).to eql my_objects end end end
This may seem a bit opaque (as a reader) – how does the sorting relate to the test? Also, if you looking at timestamps (like sort by
created_at), and you’re using
FactoryGirl.create, you may end up with duplicate timestamps. Suddenly your sort will be ill defined because the secondary sort key is undefined. A better method would be to check that the :created_at time is monotonically decreasing.
An array x is monotonically decreasing if and only if:
For these kinds of cases, I decided to write a some RSpec matchers. Following the RSpec Wiki docs here, it was a snap. I wrote matchers for monotonically increasing/decreasing and strictly increasing/decreasing tests.
The following four matchers (available from this gist (included below)) give you
be_monotonically_increasing be_strictly_increasing be_monotonically_decreasing be_strictly_decreasing
So now I can rewrite my spec
describe MyObject describe "scope #ordered" do it "returns results in most recent order" # the most recent object should have the largest created_by timestamp expect(MyObject.ordered.map(&:created_by)).to be_monotonically_decreasing end end end
These matchers follow the definitions from NIST for monotonically/strictly increasing/decreasing (see links below). The short story is:
Monotonically Increasing means:
Strictly Increasing means:
Monotonically Decreasing means:
Strictly Decreasing means:
To use these, put the gist (below)
custom_array_matchers.rb file in your
spec/support directory and you’re off to the races. You can start using those matchers in your specs. The gist also includes specs that test the matchers. If the matcher names aren’t clear, check out the specs and hopefully that’ll reveal what they do. The algorithm itself is pretty simple – take a derivative of the array values and make sure that it’s always positive (or negative) in slope for increasing (or decreasing) checks.
The code is built for RSpec 2.something (at a minimum). They also assume your sort attribute is an number. I haven’t tried it with strings, but I think it wouldn’t take much to modify things to handle more general cases. Perhaps I’ll run into that problem and have an update. Until then, they are what they are.