I have several RSpec examples that share the following complex expectation, with the array records
and the floating point numbers min_long
, max_long
, min_lat
, max_lat
varying between these examples.
expect(records).to all have_attributes(
shape: have_attributes(
exterior_ring: have_attributes(
points: all(
have_attributes(
longitude: be_between(min_long, max_long),
latitude: be_between(min_lat, max_lat)
)
)
)
)
)
(The expectation checks whether all records the respective test produced have a shape (an RGeo Polygon, in my case) completely contained in a test-specific bounding box.)
To reduce repetition and to make the intend of the complex expectation clearer by sticking a name on it, I extracted it into a method:
def expect_in_bbox(records, min_long, max_long, min_lat, max_lat)
expect(records).to all have_attributes(
shape: have_attributes(
exterior_ring: have_attributes(
points: all(
have_attributes(
longitude: be_between(min_long, max_long),
latitude: be_between(min_lat, max_lat)
)
)
)
)
)
end
This works fine, but now I have to call that method with e.g.
expect_in_bbox(valid_records, 12.55744, 12.80270, 51.36250, 51.63187)
in my examples.
This looks foreign in RSpec's specification DSL. I'd prefer to be able to write
expect(valid_records).to be_in_bbox(12.55744, 12.80270, 51.36250, 51.63187)
or
expect(valid_records).to all be_in_bbox(12.55744, 12.80270, 51.36250, 51.63187)
instead.
Is there a recommended way to achieve this?
I don't think I can use RSpec's matcher aliasing facilities for this, as they only seem to map matcher names onto other matcher names, not complete matcher invocations with arguments. Though, maybe the options
argument of alias_matcher
is meant for that?
Of course, I could also implement a custom matcher, but then I probably would be forced to provide an implementation that returns a boolean which contradicts it being composed of already existing matchers. (Not that it'd be hard, but I like the implementation using stuff like all
and be_between
.)
Lastly, I could also monkey-patch the class of the element of valid_records
to have a in_bbox?(min_long, max_long, min_lat, max_lat)
attribute, so that RSpec would automatically provide the corresponding be_in_bbox(min_long, max_long, min_lat, max_lat)
matcher.