Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Negating a custom RSpec matcher that contains expectations

I have a custom RSpec matcher that checks that a job is scheduled. It's used like so:

expect { subject }.to schedule_job(TestJob)

schedule_job.rb:

class ScheduleJob
  include RSpec::Mocks::ExampleMethods

  def initialize(job_class)
     @job_class = job_class
  end

  ...

 def matches?(proc)
   job = double
   expect(job_class).to receive(:new).and_return job
   expect(Delayed::Job).to receive(:enqueue).with(job)
   proc.call
   true
 end

This works fine for positive matching. But it does not work for negative matching. e.g:

expect { subject }.not_to schedule_job(TestJob) #does not work

For the above to work, the matches? method needs to return false when the expectations are not met. The problem is that even if it returns false, the expectations have been created regardless and so the test fails incorrectly.

Any ideas on how to make something like this work?

like image 708
user1063998 Avatar asked Dec 02 '25 09:12

user1063998


1 Answers

I had to look for it, but I think it's nicely described here in the rspec documentation

Format (from the docs) for separate logic when using expect.not_to:

RSpec::Matchers.define :contain do |*expected|
  match do |actual|
    expected.all? { |e| actual.include?(e) }
  end

  match_when_negated do |actual|
    expected.none? { |e| actual.include?(e) }
  end
end

RSpec.describe [1, 2, 3] do
  it { is_expected.to contain(1, 2) }
  it { is_expected.not_to contain(4, 5, 6) }

  # deliberate failures
  it { is_expected.to contain(1, 4) }
  it { is_expected.not_to contain(1, 4) }
end
like image 91
Axe Avatar answered Dec 04 '25 22:12

Axe