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:
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’.
Spec:
Here’s a gist incase the inline formatting sucks: after.rb · GitHub
mailer.should_receive(message).with(*@with).and_return(mail)
Make sense?
Definitely, although I’m sure I’m not the only one verifying Mailers
in this way, it’d be nice to have a library.
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.
This is certainly an interesting point that you bring up. I had never
really considering the “why” in why we write controller specs like we
do.
On Thu, Jan 27, 2011 at 2:05 PM, Michael G. [email protected]
wrote:
Mailer.deliver_job_application(job.id, user.id)
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.
Failure/Error: Mailer.should_not
deliver(:job_application).with(@job.id, @user.id)
expected Mailer not to deliver :candidate_abandon_message
Please ignore the fact that the failure message
(candidate_abandon_message) is incorrect, I was adjusting my examples
for consistency.
Spec:
Here’s a gist incase the inline formatting sucks: after.rb · GitHub
mailer.should_receive(message).with(*@with).and_return(mail)
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.
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.
On a recent Rails 3 project we implemented a matcher for sending email
but without using any stubs. It looks something like this:
RSpec::Matchers.define :send_mail do
match do |block|
ActionMailer::Base.deliveries = []
block.call
ActionMailer::Base.deliveries.count.should == 1
mail = ActionMailer::Base.deliveries.last
# now inspect the mail object as you see fit
end
end
Then in our spec we have:
expect { some_func }.to send_email
We also have chains for recipients, subject, content, and so on.
HTH,
Mike
This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.