[rspec-users] feedback on matcher

David Chelimsky dchelimsky at gmail.com
Thu Jan 27 10:30:57 EST 2011


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?

> Would it have been better to adapt the
> Mailer interface to comply with the specs?

That's a funny thing. There are a lot of things that I think would work differently in Rails API's if they were driven out with a TDD mindset. But the API's seem to be designed with rapid prototyping in mind more than long term maintainability. For example, I'd really like to be able to write a controller example like this:

# not anything close to reality
controller = ThingsController.new
response = controller.action(:some => 'params')
response.should be_redirect_to(:action => 'other')

But we don't really have a public API for instantiating controllers, which is why we have the http verb methods, which conflate routing concerns with controller concerns.

I'm not judging these choices as good or bad. Just observing one aspect of the resulting 'feel'.

FWIW,
David


More information about the rspec-users mailing list