我正在尝试测试以确保通知邮件程序在无效记录之后没有发送但我在测试完成之前一直低于错误
“的ActiveRecord :: RecordInvalid:
it 'does not call send_email_notification' do
expect(NotificationMailer).not_to receive(:user_notification)
FactoryGirl.create(:invalid_user, shop: shop)
end
我该如何正确测试?
编辑:这是邮件发送的代码:
after_create :send_email_notification
private
def send_email_notification
if self.shop.email_notifications
NotificationMailer.user_notification(self).deliver_now
end
end
end
答案 0 :(得分:2)
it 'does not send notification email when user is invalid' do
expect(NotificationMailer).not_to receive(:user_notification)
post :create, user: attributes_for(:invalid_user)
end
所以,这样做是为了让您按照自己的方式做出预期,然后发布到 user_controller 创建方法 invalid_user 属性。
当然,如果您已在用户模型中正确设置验证,并且随后未调用 NotificationMailer.user_notification ,则不应允许该帖子创建记录。
请注意, attributes_for 是另一种 FactoryGirl 方法,您可以使用该方法排列工厂属性并将其作为控制器参数传递。
现在!为什么它不能用你原来的方法? 这是因为FactoryGirl抱怨它无法创建记录,这是绝对符合逻辑的,因为您正在尝试创建无效用户。失败的错误与测试您的电子邮件通知无关,而是与您设置工厂的方式无关。
最后的说明!如果您运行测试并抱怨:
"NoMethodError: undefined method `post' for #<RSpec::ExampleGroups"
这可能意味着您的spec文件不在spec / controllers下。
发布,创建,修补,删除方法是RSpec::Rails::ControllerExampleGroup
要解决此问题,请参阅以下Stackoverflow answer
希望这有帮助。
答案 1 :(得分:0)
下面是我用来测试您的用例的一些代码:您可以将其复制并粘贴到文件中并在其上运行rspec
。我希望我对你未公开的Rails应用程序部分做出的假设并不是太远了。
require 'active_record'
require 'factory_girl'
require 'rspec'
ActiveRecord::Base.establish_connection(
adapter: 'sqlite3', database: ':memory:'
)
class User < ActiveRecord::Base
has_one :shop
validates :email, presence: true
after_create :send_notification
private
def send_notification
if shop.email_notifications
NotificationMailer.user_notification(self).deliver_now
end
end
end
class Shop < ActiveRecord::Base
belongs_to :user
end
ActiveRecord::Schema.define do
create_table :users do |t|
t.string :email
end
create_table :shops do |t|
t.boolean :email_notifications
t.belongs_to :user
end
end
FactoryGirl.define do
factory :user do
email "test@example.com"
shop
factory :invalid_user do
email nil
end
end
factory :shop do
email_notifications true
end
end
RSpec.describe User do
context 'on save' do
let(:mailer) { double('mailer') }
before do
# You probably won't need this stub_const since the class will exist
# in your app
stub_const('NotificationMailer', Class.new)
allow(NotificationMailer).to \
receive(:user_notification).with(user).and_return(mailer)
end
context 'when user is valid' do
let(:user) { FactoryGirl.build(:user) }
it 'calls to send email notifications' do
expect(mailer).to receive(:deliver_now)
user.save
end
end
context 'when user is invalid' do
let(:user) { FactoryGirl.build(:invalid_user) }
it 'does not call to send email notifications' do
expect(mailer).to_not receive(:deliver_now)
user.save
end
end
end
end
由于你的回调中有一个外部依赖项(对单独的类NotificationMailer
的标注),你可能需要将消息存根到该依赖项以使测试通过,否则你可能会得到当您可能不期望它们时会返回nil
个值(有关详细信息,请参阅this blog post)。
只是一个观点,但你甚至可以做未来 - 如果你只在there are no external dependencies in them and the logic only refers to state internal to the object(在这种情况下为User
)时使用回调,那么你会受到青睐。您所做的更改就像将NotificationMailer.user_notification(self).deliver_now
调用移出User
模型回调并进入控制器(我假设)您正在调用以保存用户。提取可能看起来像:
def create
@user = User.new(user_params)
if @user.save
NotificationMailer.user_notification(@user).deliver_now
# do other stuff, render, redirect etc
else
# do something else
end
end