I want to test my Mailer model.
Letters should be sent to multiple recipients.
How to test that messages sent to multiple recipients?
I created spec file:
describe SubscribeMailer do
before(:each) do
(1..3).to_a.each do
FactoryGirl.create(:subscriber)
end
end
let(:post) { #user.posts.create(#attr)}
let(:mail) { SubscribeMailer.new_post(post) }
#Failure/Error for this test
it 'subscribers' do
mail.to.should == Subscriber.all.map(&:email)
end
end
But I don't know how to write the test.
In addition to #Trevoke answer, I'd tell you to have a look at this excellent Railscast.
In test environment, ActionMailer.deliveries is an array of mail objects that would otherwise have been sent.
Related
Hi i have written Rspec to validate a body of message\n
describe "validate_msm" do
let!(:user) { FactoryGirl.create(:user) }
it "should contains welcome user" do
body = user.send_sms("909090990")
expect(body).to include("welcome")
end
end
as send_sms will call the send method which i have mentioned in let!
def send_sms
...
..
body="welcome user"
Message.send(contact, body)
end
so how to check with the body content is equal to welcome user ,
as send_sms doesn't return anything, so how to checks the value present in the body variable in rspec
You don't. Or rather you don't that easily. Most of the libraries like this should come together with test adapters and helpers to make such testing possible. If this one does not, you can only test that the message has been sent with correct arguments:
it "should contains welcome user" do
allow(Message).to receive(:send)
user.send_sms("909090990")
expect(Message).to have_received(:send) do |_contact, body|
expect(body).to include "welcome"
end
end
I am new to Rspec, i am trying to write a test for a simple feature.
A user creates a contract, if the contract the is created and has a specific value on its property then i send an email to notify the teacher.
How do i write the condition in the test?
I am using letter_opener and ActionMailer.
describe "When a user creates a apprentice contract" do
let(:admin) { users(:admin) }
before { signin admin }
it "should send an email to the teacher" do
contract = create(:contract)
contract.education_type.must_equal("company_apprentice")
end
end
when contract.education_type.must_equal("school_apprentice") is true i want to test if it sends an email.
How do i write that in this test?
it 'should send an email to the teacher' do
expect { create(:contract, education_type: 'company_apprentice' }
.to change { ActionMailer::Base.deliveries.count }
.by(1)
end
you also need this set:
# config/environments/test.rb
config.action_mailer.delivery_method = :test
I'm writing integration tests using Rspec and Capybara. I've noticed that quite often I have to execute the same bits of code when it comes to testing the creation of activerecord options.
For instance:
it "should create a new instance" do
# I create an instance here
end
it "should do something based on a new instance" do
# I create an instance here
# I click into the record and add a sub record, or something else
end
The problem seems to be that ActiveRecord objects aren't persisted across tests, however Capybara by default maintains the same session in a spec (weirdness).
I could mock these records, but since this is an integration test and some of these records are pretty complicated (they have image attachments and whatnot) it's much simpler to use Capybara and fill out the user-facing forms.
I've tried defining a function that creates a new record, but that doesn't feel right for some reason. What's the best practice for this?
There are a couple different ways to go here. First of all, in both cases, you can group your example blocks under either a describe or context block, like this:
describe "your instance" do
it "..." do
# do stuff here
end
it "..." do
# do other stuff here
end
end
Then, within the describe or context block, you can set up state that can be used in all the examples, like this:
describe "your instance" do
# run before each example block under the describe block
before(:each) do
# I create an instance here
end
it "creates a new instance" do
# do stuff here
end
it "do something based on a new instance" do
# do other stuff here
end
end
As an alternative to the before(:each) block, you can also use let helper, which I find a little more readable. You can see more about it here.
The very best practice for your requirements is to use Factory Girl for creating records from a blueprint which define common attributes and database_cleaner to clean database across different tests/specs.
And never keep state (such as created records) across different specs, it will lead to dependent specs. You could spot this kind of dependencies using the --order rand option of rspec. If your specs fails randomly you have this kind of issue.
Given the title (...reusing code in Rspec) I suggest the reading of RSpec custom matchers in the "Ruby on Rails Tutorial".
Michael Hartl suggests two solutions to duplication in specs:
Define helper methods for common operations (e.g. log in a user)
Define custom matchers
Use these stuff help decoupling the tests from the implementation.
In addition to these I suggest (as Fabio said) to use FactoryGirl.
You could check my sample rails project. You could find there: https://github.com/lucassus/locomotive
how to use factory_girl
some examples of custom matchers and macros (in spec/support)
how to use shared_examples
and finally how to use very nice shoulda-macros
I would use a combination of factory_girl and Rspec's let method:
describe User do
let(:user) { create :user } # 'create' is a factory_girl method, that will save a new user in the test database
it "should be able to run" do
user.run.should be_true
end
it "should not be able to walk" do
user.walk.should be_false
end
end
# spec/factories/users.rb
FactoryGirl.define do
factory :user do
email { Faker::Internet.email }
username { Faker::Internet.user_name }
end
end
This allows you to do great stuff like this:
describe User do
let(:user) { create :user, attributes }
let(:attributes) { Hash.new }
it "should be able to run" do
user.run.should be_true
end
it "should not be able to walk" do
user.walk.should be_false
end
context "when user is admin" do
let(:attributes) { { admin: true } }
it "should be able to walk" do
user.walk.should be_true
end
end
end
I need to receive incoming emails as multipart-formdata via a POST request from Cloudmailin. The POST resembles the following:
Parameters: {"to"=>"<email#exmaple.comt>", "from"=>"whomever#example", "subject"=>"my awesome subject line....
Actually, receiving and parsing emails is super easy because the email is just posted as params: params[:to], params[:from], etc. However, how do I simulate this POST request in rails?
I built a dummy rails app to test out Cloudmailin, so I have an actual request. However, it's a 6k character file, so I'd like to load this file as the parameters of the POST request. I've tried using the built rails post and post_via_redirect methods to load a file, but it escapes all of the parameters( \"to\"), which is no good. Any ideas?
So, I ended up doing:
#parameters = { "x_to_header"=>"<#{ #detail.info }>",
"to"=>"<#{ #account.slug }#cloudmailin.net>",
"from"=>"#{ #member.email }",
"subject"=>"meeting on Monday",
"plain"=>"here is my message\nand this is a new line\n\n\nand two new lines\n\n\n\nand a third new line"
}
then just:
post "/where_ever", #parameters
seems to get the job done for now
A simple way would probably to execute a script in capybara. Just make sure with the #javascript tag, then load any page in your app that has jQuery installed (technically, you don't need this, but it's much easier. Then:
When /^I get a post request from Cloudmailin$/ do
visit '/some/page/with/jquery'
page.execute_script(%{$.post("/some/path?to=some_email&etc=etc");})
end
There's the simple post capybara method too, but I'm not too sure about how that works. Might be worth looking into.
I saw this answer last night when I was updating some of my own test code for Rails 3.2.8, and which uses the Mail gem, and thought I'd share what I found. The test code is for an application that needs to take a POST from Cloudmailin and then process it to create a new user with Devise, and then send a confirmation to that user, which the user can then follow to choose a password. Here is my controller spec:
require 'spec_helper'
describe ThankyouByEmailController do
message1 = Mail.new do
from "Frommy McFromerton <frommy.mcfrommerton#gmail.com>"
to "toey.receivesalot#gmail.com"
subject "cloudmailin test"
body 'something'
text_part do
body 'Here is the attachment you wanted'
end
html_part do
content_type 'text/html; charset=UTF-8'
body '<h1>Funky Title</h1><p>Here is the attachment you wanted</p>'
end
end
describe "creating new users" do
describe "unregistered FROM sender and Unregistered TO receiver" do
it "should create 2 new users" do
lambda do
post :create, :message => "#{#message1}"
end.should change(User, :count).by(2)
end
end
end
end
Hope this clean up your own tests. And for anyone else interested in testing the mail gem, mikel's documentation has come a long way for same:
https://github.com/mikel/mail
Currently in my tests I do something like this to test if an email is queued to be sent
assert_difference('ActionMailer::Base.deliveries.size', 1) do
get :create_from_spreedly, {:user_id => #logged_in_user.id}
end
but if i a controller action can send two different emails i.e. one to the user if sign up goes fine or a notification to admin if something went wrong - how can i test which one actually got sent. The code above would pass regardless.
As of rails 3 ActionMailer::Base.deliveries is an array of Mail::Message's. From the mail documentation:
# mail['from'] = 'mikel#test.lindsaar.net'
# mail[:to] = 'you#test.lindsaar.net'
# mail.subject 'This is a test email'
# mail.body = 'This is a body'
#
# mail.to_s #=> "From: mikel#test.lindsaar.net\r\nTo: you#...
From that it should be easy to test your mail's in an integration
mail = ActionMailer::Base.deliveries.last
assert_equal 'mikel#test.lindsaar.net', mail['from'].to_s
assert_equal 'you#test.lindsaar.net', mail['to'].to_s
When using the ActionMailer during tests, all mails are put in a big array called deliveries. What you basically are doing (and is sufficient mostly) is checking if emails are present in the array.
But if you want to specifically check for a certain email, you have to know what is actually stored in the array. Luckily the emails themselves are stored, thus you are able to iterate through the array and check each email.
See ActionMailer::Base to see what configuration methods are available, which you can use to determine what emails are present in the array. Some of the most suitable methods for your case probably are
recipients: Takes one or more email addresses. These addresses are where your email will be delivered to. Sets the To: header.
subject: The subject of your email. Sets the Subject: header.
Using current Rspec syntax, I ended up using the following:
last_email = ActionMailer::Base.deliveries.last
expect(last_email.to).to eq ['test#example.com']
expect(last_email.subject).to have_content 'Welcome'
The context of my test was a feature spec where I wanted to make sure a welcome email was sent to a user after signing up.
As of 2020 (Rails 6 era, probably introduced earlier) you can do the following:
(using a SystemTest example) TL;DR: use assert_emails from ActionMailer::TestHelper and ActionMailer::Base.deliveries.last to access the mail itself.
require "application_system_test_case"
require 'test_helper'
require 'action_mailer/test_helper'
class ContactTest < ApplicationSystemTestCase
include ActionMailer::TestHelper
test "Send mail via contact form on landing page" do
visit root_url
fill_in "Message", with: 'message text'
# Asserting a mail is sent
assert_emails 1 do
click_on "Send"
end
# Asserting stuff within that mail
last_email = ActionMailer::Base.deliveries.last
assert_equal ['whatever'], last_email.reply_to
assert_equal "contact", last_email.subject
assert_match /Mail from someone/, last_email.body.to_s
end
end
Official doc:
ActionMailer Guide/Testing
Testing Guide/ActionMailer
Note
Instead of manually checking the content of the mail as in the system test above, you can also test whether a specific mailer action was used, like this:
assert_enqueued_email_with ContactMailer, :welcome, args: ["Hello", "Goodbye"]
And some other handy assertion, see https://api.rubyonrails.org/v6.0.3.2/classes/ActionMailer/TestHelper.html#method-i-assert_emails .
The test framework shoulda has an excellent helper which lets you assert certain conditions about an email that was sent. Yes, you could do it yourself with ActionMailer.deliveries, but shoulda makes it all one neat little block
A little late, but it may help others:
You could use Email-spec, a collection of Rspec/Minitest matchers and Cucumber steps.
Here is the best way I've found to do it.
1) Include the action mailer callbacks plugin like this:
script/plugin install git://github.com/AnthonyCaliendo/action_mailer_callbacks.git
I don't really use the plugin's main features, but it does provide the nice functionality of being able to figure out which method was used to send an email.
2) Now you can put some methods in your test_helper.rb like this:
def assert_sent(method_name)
assert sent_num_times(method_name) > 0
end
def assert_not_sent(method_name)
assert sent_num_times(method_name) == 0
end
def assert_sent_once(method_name)
assert sent_num_times(method_name) == 1
end
def sent_num_times(method_name)
count = 0
#emails.each do |email|
count += 1 if method_name == email.instance_variable_get("#method_name")
end
count
end
3) Now you can write sweet tests like this:
require 'test_helper'
class MailingTest < ActionController::IntegrationTest
def setup
#emails = ActionMailer::Base.deliveries
#emails.clear
end
test "should send a mailing" do
assert_difference "Mailing.count", 1 do
feeds(:feed1).generate_mailing
end
assert_sent_once "broadcast"
assert_not_sent "failed_mailing"
end
end
Here "broadcast" and "mailing_failed" are the names of the methods in my ActionMailer::Base class. These are the ones you normally use by calling Mailer.deliver_broadcast(some_data) or Mailer.deliver_failed_mailing(some_data) etc. That's it!