Using a Custom RSpec Matcher with RSpec mocks
I wanted to check whether a value has been passed to a mock in my code, the solution seems simple using the with method.  I have isolated the bit I am interested in to reduce the noise.
def save!(document)
    service.save(document)
end
For the test I would normally I would use the standard with method and pass the expected value.
expect(mock).to_receive(:save!).with(expected_document)
However my expected object contains a Date.  As the with method calls the == the comparisson fails.
The documentation includes lots of examples I could use like kind_of(Class) or anything() but then I'm not testing much.
Custom Matchers.
As the with method accepts any RSpec Matcher, a custom matcher solves the problem.
At the top of the file add a custom matcher that checks the document_id is the same in the actual and expected object and that the created and updated times are within the last 60 seconds.
RSpec::Matchers.define :other_document do |expected|
  match do |actual|
    actual.user_id == expected.user_id &&
      actual.document_id == expected.document_id &&
      actual.created.between?(Time.now - 60, Time.now)
      actual.updated.between?(Time.now - 60, Time.now)
  end
end
We can then use the custom matcher in our mock expectation
expect(mock).to_receive(:save!).with(other_document(expected_document))
Which then passes ✅
© Peter Grainger.RSS