user1063998
user1063998

Reputation: 602

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?

Upvotes: 3

Views: 2020

Answers (1)

Axe
Axe

Reputation: 692

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

Upvotes: 6

Related Questions