在 Rspec 中模拟 ActionMailer

8

你好!
我正在尝试使用Rspec测试一个控制器方法,如下所示:

def email_customer
  @quote = Quote.find(params[:quote_id])
  hash = {
    quote: @quote,
    body: params[:email_body],
    subject: params[:email_subject]
  }
  QuoteMailer.email_customer(hash).deliver
  redirect_to edit_quote_path params[:quote_id]
end

对应的规范如下:

describe 'POST email_customer' do
  let!(:quote) { create(:valid_quote) }
  it 'assigns the quote and sends the customer an email' do
    post :email_customer, quote_id: quote.id
    expect(assigns(:quote)).to eq(quote)
    expect(QuoteMailer).to receive(:email_customer).with(an_instance_of(Hash)).and_return( double('QuoteMailer', deliver: true))
  end
end

当测试运行时,我收到了这个消息:
Failure/Error: expect(QuoteMailer).to receive(:email_customer).with(an_instance_of(Hash)).and_return( double('QuoteMailer', deliver: true))
       (<QuoteMailer (class)>).email_customer(an instance of Hash)
           expected: 1 time with arguments: (#<RSpec::Matchers::AliasedMatcher:0x0000000c2b1e28 @description_block=#<Proc:0x00000009816268@/home/david/.rvm/gems/ruby-2.1.1/gems/rspec-expectations-3.0.0.beta2/lib/rspec/matchers.rb:231 (lambda)>, @base_matcher=#<RSpec::Matchers::BuiltIn::BeAnInstanceOf:0x0000000c2b1e50 @expected=Hash>>)
           received: 0 times with arguments: (#<RSpec::Matchers::AliasedMatcher:0x0000000c2b1e28 @description_block=#<Proc:0x00000009816268@/home/david/.rvm/gems/ruby-2.1.1/gems/rspec-expectations-3.0.0.beta2/lib/rspec/matchers.rb:231 (lambda)>, @base_matcher=#<RSpec::Matchers::BuiltIn::BeAnInstanceOf:0x0000000c2b1e50 @expected=Hash>>)
     # ./spec/controllers/quotes_controller_spec.rb:28:in `block (3 levels) in <top (required)>'

我在控制器方法和发送邮件方法中使用了puts语句,以便知道它会运行并调用正确的方法,但我不确定为什么它失败了。我猜测这是一个愚蠢的语法错误,但我不确定。非常感谢您的帮助!


post 之前编写你的 expect - Uri Agassi
啊,我之前尝试在发布之前放置两个 expects,但是这会导致关于 @quote 分配的错误,但是在发布之前放置 stub 就可以完美地工作了!谢谢您!如果您将其作为答案,我会接受它。 - davidicus
1个回答

12

在调用要测试的方法之前,应该先写出期望的信息,因为它们实际上是在对象中存根化的消息:

describe 'POST email_customer' do
  let!(:quote) { create(:valid_quote) }
  it 'assigns the quote and sends the customer an email' do
    expect(QuoteMailer).to receive(:email_customer).with(an_instance_of(Hash)).and_return( double('QuoteMailer', deliver: true))

    post :email_customer, quote_id: quote.id

    expect(assigns(:quote)).to eq(quote)
  end
end

在提交之前使用expect(assigns(:quote))。to eq(quote)导致了这个错误: “Failure / Error:expect(assigns(:quote))。to eq(quote):期望值:#<Quote id: 1, email: "test@example.com", phone_number: nil, first_name: "test", last_name: "mctesterson", company: nil, twitter: nil, name: nil, valid_until_date: "2014-07-24 20:00:37", estimated_delivery_date: "2014-07-24 20:00:37", salesperson_id: 2, store_id: 3, deleted_at: nil, created_at: "2014-07-23 20:00:38", updated_at: "2014-07-23 20:00:38">实际值:nil” - davidicus
@davidicus - 可能它只是未通过测试吗?(接收到的值为 nil - Uri Agassi
不行,当我将 expect(assigns(:quote)).to eq(quote) 移动到 post :email_customer, quote_id: quote.id _之后_,它就通过了。 - davidicus
1
@davidicus - 对不起,我误导了你。expect(assigns...应该在调用之后,因为它不是消息期望...只有消息期望应该在调用之前...我已经更新了我的答案。 - Uri Agassi
@UriAgassi - 我的Action Mailer需要params哈希 - 你知道如何在RSpec Mailer测试中直接存根它吗? - BenKoshy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接