[rspec-users] feedback on matcher

Michael Guterl mguterl at gmail.com
Thu Jan 27 14:05:59 EST 2011


On Thu, Jan 27, 2011 at 10:30 AM, David Chelimsky <dchelimsky at gmail.com> wrote:
>
> On Jan 27, 2011, at 7:48 AM, Michael Guterl wrote:
>
>> We have moved from Rails 2 to 3 and the changing Mailer syntax has
>> caused us to rewrite some of our specs.
>>
>> In Rails 2:
>>
>> Implementation:
>>  Mailer.deliver_job_application(job.id, user.id)
>>
>> Spec:
>>  Mailer.should_receive(:deliver_job_application).with(@job.id, @user.id)
>>
>> ---
>>
>> In Rails 3:
>>
>> Implementation:
>>  Mailer.job_application(job.id, user.id).deliver
>>
>> Spec:
>>  message = double
>>  message.should_receive(:deliver)
>>  Mailer.should_receive(:job_application).with(@job.id,
>> @user.id).and_return(message)
>>
>> ---
>>
>> I turned the latter example into a matcher for RSpec 2 and I'm open
>> for feedback.
>>
>> Here's a gist incase the inline formatting sucks: https://gist.github.com/798513
>>
>> RSpec::Matchers.define :deliver do |message|
>>  chain :with do |*args|
>>    @with = args
>>  end
>>
>>  match do |mailer|
>>    mail = double
>>    mail.should_receive(:deliver)
>>
>>    mailer.should_receive(message).with(*@with).and_return(mail)
>>  end
>> end
>>
>> Mailer.should deliver(:job_application).with(@job.id, @user.id)
>>
>> ---
>>
>> Is this a sane approach?
>
> I think it's sane for inside your own app, but not as part of a lib. First, it's bound to rspec-mocks, and including it in an rspec lib would require extra handling to either make it only available for rspec-mocks or make it support the other frameworks that rspec supports. Second, it hides a message expectation. Again, that's fine for your own app, in which you know what's going on, but would confuse some users if it were in a lib.
>
> Make sense?
>

Once you confirmed my sanity, I started implementing this matching in
the rest of the project.  Everything was fine until I came to an
example that negates the expectation with should_not.

Mailer.should_not deliver(:job_application).with(@job.id, @user.id)

Failure/Error: Mailer.should_not
deliver(:job_application).with(@job.id, @user.id)
  expected Mailer not to deliver :candidate_abandon_message

I can fix this by adding another matcher:

RSpec::Matchers.define :not_deliver do |message|
  match do |mailer|
    mailer.should_not_receive(message).with(*@with)
  end
end

but this feels awfully hacky and unconventional.  I know Capybara has
to do something similar with has_content / has_no_content and I know
my team (and myself) never seem to get this correct.

Is there a better way to do this?

Best,
Michael Guterl


More information about the rspec-users mailing list