Ruby on rails Rspec:测试以确保通知电子邮件在无效记录之后*未*发送(ActiveRecord::RecordInvalid:)

Ruby on rails Rspec:测试以确保通知电子邮件在无效记录之后*未*发送(ActiveRecord::RecordInvalid:),ruby-on-rails,rspec,Ruby On Rails,Rspec,我正在尝试测试,以确保邮件程序在无效记录之后没有发送通知,但在测试完成之前,我一直在获取以下错误信息 “ActiveRecord::RecordInvalid: 我怎样才能正确地测试它 编辑:以下是发送邮件的代码: after_create :send_email_notification private def send_email_notification if self.shop.email_notifications NotificationMai

我正在尝试测试,以确保邮件程序在无效记录之后没有发送通知,但在测试完成之前,我一直在获取以下错误信息

“ActiveRecord::RecordInvalid:

我怎样才能正确地测试它

编辑:以下是发送邮件的代码:

after_create :send_email_notification


  private


  def send_email_notification
    if self.shop.email_notifications
        NotificationMailer.user_notification(self).deliver_now
    end
  end
end
因此,这样做的目的是设置您的期望值,然后无效用户属性发布到用户控制器创建方法

当然,如果您在用户模型中正确设置了验证,并且随后没有调用NotificationMailer.user\u notification,则不应允许帖子创建记录

请注意,attributes\u for是另一种FactoryGirl方法,您可以使用该方法将工厂属性作为控制器参数进行排列和传递

现在!为什么它不适用于您原来的方法? 这是因为FactoryGirl抱怨它无法创建记录,这是绝对合乎逻辑的,因为您正在尝试创建无效用户。失败的错误与测试电子邮件通知无关,而是与您设置Factory的方式有关

最后一点注意!如果您运行测试时,它会抱怨:

"NoMethodError: undefined method `post' for #<RSpec::ExampleGroups"

“NoMethodError:undefined method`post'for#下面是我用来测试用例的一些代码:您可以将其复制并粘贴到文件中,然后在其上运行
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

由于触发了异常,这意味着存在验证问题,这是您尝试保存无效的_用户后的预期行为。这本身就确保了没有邮件被发送。问题是您在代码中的何处声明了发送电子邮件?是在保存之后,还是在创建之后。那么,我该如何为此编写规范呢?期望无效用户无效?你能分享你实际发送邮件的部分代码吗。等等-现在我得到“NoMethodError:未定义#的方法'post';这可能是因为您的规范不在规范/控制器之下。我已编辑了答案,以包含指向stackoverflow答案的链接,该答案演示了如何解决此问题的说明。
"NoMethodError: undefined method `post' for #<RSpec::ExampleGroups"
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
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