Forum: RSpec Cucumber - Testing ActionMailer

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
171ea139761951336b844e708d1547ab?d=identicon&s=25 James Byrne (byrnejb)
on 2009-04-27 22:44
I need some help with this.  I have installed email-spec (courtesy of
BMabey) and I have configure things thusly:

  Scenario: E-Mail Exchange Rates to notify parties
    Given a currency exchange feed from the Bank of Canada
    When the currency exchange retrieval script runs
    Then I should receive an email

Then /should receive (an|\d+) e-?mails?/ do |amount|
  amount = 1 if amount == "an"
  unread_emails_for(current_email_address).size.should == amount.to_i
end


../environments/test.rb
# Tell ActionMailer not to deliver emails to the real world.
# The :test delivery method accumulates sent emails in the
# ActionMailer::Base.deliveries array.
config.action_mailer.delivery_method = :test
config.gem 'bmabey-email_spec', :lib => 'email_spec'


class MailerPublic < ActionMailer::Base
  def forex_daily_notice(address,rates)
    rates_effective = Date.today.to_s(:db)
    recipients  address.to_a
    from        "ForexService@harte-lyne.ca"
    reply_to    "support@harte-lyne.ca"
    subject     "#{rates_effective} - Foreign Exchange Rates Notice"
    body        :rates_effective => rates_effective, :rates => rates
  end
end


./views/mailer_public/forex_daily_notice.text.plain.erb
Canada Customs foreign currency exchange rates for
currency conversion of shipments made on <%=@rates_effective%>

When I run this

./bin/script.rb
...
# Format and email the results.
if fx_hash_array
  puts "got an array"
  send_to = 'test_email@example.com'
  MailerPublic.deliver_forex_daily_notice!(send_to,fx_hash_array)
  puts ActionMailer::Base.deliveries.length
  ActionMailer::Base.deliveries.each { |m| puts m }
end

Then I see this:

got an array
0

This is my first attempt at using ActionMailer so no doubt I have
overlooked something basic.  Can anyone tell me what it is?
C694a032be7518a0d704318895f8fe1d?d=identicon&s=25 Ben Mabey (mabes)
on 2009-04-28 06:57
(Received via mailing list)
James Byrne wrote:
>   unread_emails_for(current_email_address).size.should == amount.to_i
>
>
> if fx_hash_array
> 0
>
> This is my first attempt at using ActionMailer so no doubt I have
> overlooked something basic.  Can anyone tell me what it is?
>


Looks right to me.  To be honest though I have not used ActionMailer
outside of Rails (where it just works out of the box).  I don't see
anything wrong with the approach you are taking.  Maybe the rails
mailing list could provide better advice.   Sorry, I couldn't be of any
help.

-Ben
171ea139761951336b844e708d1547ab?d=identicon&s=25 James Byrne (byrnejb)
on 2009-04-28 15:43
Ben Mabey wrote:
>   Sorry, I couldn't be of any help.
>

Actually, you were a great help, but on a different thread.  The reason
I was not seeing anything is that the default environment is
"development" and I have things configured so that the deliveries array
is only used in "test".

Adding "puts" in front of the call to %x() in the step definition file
showed that the email is generated as expected.  I have something else
wrong with my test, probably the address that I am checking for,
171ea139761951336b844e708d1547ab?d=identicon&s=25 James Byrne (byrnejb)
on 2009-04-28 16:43
I have reached this point in testing using email-spec:

The code:

# Format and email the results.
if fx_hash_array
  send_to = 'email@example.com'
  MailerPublic.deliver_forex_daily_notice!(send_to,fx_hash_array)
  ActionMailer::Base.deliveries.each { |m| puts m } unless
Rails.env.production?
end

The Feature Scenario:

  Scenario: E-Mail Exchange Rates to notify parties
    Given a currency exchange feed from the Bank of Canada
      And an empty email queue
    When the currency exchange retrieval script runs
    Then I should receive an email

The Steps:

Then /currency exchange retrieval script runs/ do
  stdout = %x(#{RAILS_ROOT}/bin/hll_forex_ca_feed.rb #{@xchg_source})
  puts stdout
  puts current_email_address
end

... local_email_steps

Then /should receive (an|\d+) e-?mails?/ do |amount|
  amount = 1 if amount == "an"
  unread_emails_for(current_email_address).size.should == amount.to_i
end

The output:

  Scenario: E-Mail Exchange Rates to notify parties
     # features/app/models/currency_exchange_rates/
         currency_exchange_rates.feature:19

xml source is:
/home/byrnejb/Software/Development/Projects/proforma.git/doc/data/fx-noon-all-2009-04-15.xml

    Given a currency exchange feed from the Bank of Canada
      # features/app/models/currency_exchange_rates/step_definitions/
          currency_exchange_rates_steps.rb:5

    And an empty email queue
      # features/step_definitions/local_email_steps.rb:28

From: ForexService@harte-lyne.ca
Reply-To: support@harte-lyne.ca
To: email@example.com
Subject: [ForEx] 2009-04-28 - Foreign Exchange Rates Notice
Mime-Version: 1.0
Content-Type: multipart/alternative;
boundary=mimepart_49f713ebd7061_2d4d155c1a4737c82cd


--mimepart_49f713ebd7061_2d4d155c1a4737c82cd
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: Quoted-printable
Content-Disposition: inline

Canada Customs foreign currency exchange rates for 2009-04-28:


--mimepart_49f713ebd7061_2d4d155c1a4737c82cd--

email@example.com

    When the currency exchange retrieval script runs
      # features/app/models/currency_exchange_rates/step_definitions/
          currency_exchange_rates_steps.rb:11

    Then I should receive an email
      # features/step_definitions/local_email_steps.rb:49
      expected: 1,
           got: 0 (using ==)
      Diff:
      @@ -1,2 +1,2 @@
      -1
      +0
       (Spec::Expectations::ExpectationNotMetError)
      ./features/step_definitions/local_email_steps.rb:51:in
         `/should receive (an|\d+) e-?mails?/'
      features/app/models/currency_exchange_rates/currency_exchange_rates.feature:23:in
`Then I should receive an email'

The email To: header value and the current_email_address are the same
insofar as I can tell.  But the email_spec matcher is not finding that
address in the deliveries array.  Any ideas as to what I am missing?
171ea139761951336b844e708d1547ab?d=identicon&s=25 James Byrne (byrnejb)
on 2009-04-29 16:08
James Byrne wrote:
> I have reached this point in testing using email-spec:
...
> The email To: header value and the current_email_address are the same
> insofar as I can tell.  But the email_spec matcher is not finding that
> address in the deliveries array.  Any ideas as to what I am missing?

I have determined what the problem is. I just do not know how to fix it.

What is happening is that I am testing a standalone (outside of a Rails
instance) Ruby script that requires ActionMailer.  I call this script
from inside the step definition using the %x alias for Kernel#`. When
this runs it picks up the test environment and directs email output into
ActionMailer::Base.deliveries.  However, this array only exists during
the script's existence and naturally disappears when he script
terminates. In consequence, the results are not available for testing.

I have, up to now, simply accepted the magic of injecting test methods
into classes and using the results.  Now I need to have explained how I
get this to magic work with stand alone scripts.  How do I get
ActionMailer::Base.deliveries created by ActionMailer in the script to
remain available to cucumber/email-spec?
C694a032be7518a0d704318895f8fe1d?d=identicon&s=25 Ben Mabey (mabes)
on 2009-04-29 16:38
(Received via mailing list)
James Byrne wrote:
>
> I have, up to now, simply accepted the magic of injecting test methods
> into classes and using the results.  Now I need to have explained how I
> get this to magic work with stand alone scripts.  How do I get
> ActionMailer::Base.deliveries created by ActionMailer in the script to
> remain available to cucumber/email-spec?
>

I have plans to make email-spec work with any SMTP mailer and thereby
making it more "black-box"y and allow for external processes to be
easily tested... However, that is not done yet.  As it stands now you
have two options: a) have the test execution and script in the same
process or b) Use ARMailer[1] in your testing environment.  ARMailer
places the messages in a database for queuing.  So instead of checking
the in-memory array EmailSpec will check the queue on the database.
Since this is just for testing then you could use a lightweight sqlite3
db to store this.

-Ben

1. http://github.com/seattlerb/ar_mailer/tree/master
77a052f877809ac07e5d8b7d9413d547?d=identicon&s=25 Brandt Kurowski (Guest)
on 2009-05-03 06:10
(Received via mailing list)
On Apr 29, 10:37 am, Ben Mabey <b...@benmabey.com> wrote:
> However, that is not done yet.  As it stands now you
> have two options: a) have the test execution and script in the same
> process or b) Use ARMailer[1] in your testing environment.  ARMailer
> places the messages in a database for queuing.  So instead of checking
> the in-memory array EmailSpec will check the queue on the database.  
> Since this is just for testing then you could use a lightweight sqlite3
> db to store this.

For what it's worth, I believe that ARMailer requires you to change
your base class for mailers from ActionMailer::Base to
ActionMailer::ARMailer, which makes it difficult to use ARMailer only
in your test environment. However, the adzap-ar_mailer[1] fork
supports using the ActionMailer::Base base class, which makes it easy
to use ARMailer only for testing (by specifying it in config/
environments/test.rb) while using another delivery mechanism for
development/production.

1. http://github.com/adzap/ar_mailer/tree/master
171ea139761951336b844e708d1547ab?d=identicon&s=25 James Byrne (byrnejb)
on 2009-05-03 22:19
What I ended up doing is dumping the deliveries array to a file from the
standalone script and reading it into the deliveries array inside the
step definitions file. A kludge, but it suffices.
This topic is locked and can not be replied to.