如何使用rspec测试我的邮件程序中是否设置了实例变量?

时间:2014-01-12 21:18:07

标签: ruby-on-rails rspec instance-variables

如何使用rspec测试我的邮件程序中是否设置了某个实例变量?分配回来未定义..

require File.dirname(__FILE__) + '/../../spec_helper'

describe UserMailer do

  it "should send the member user password to a User" do
    user = FG.create :user

    user.create_reset_code

    mail = UserMailer.reset_notification(user).deliver

    ActionMailer::Base.deliveries.size.should == 1  

    user.login.should be_present  

    assigns[:person].should == user
    assigns(:person).should == user #both assigns types fail
  end
end

返回的错误是:

undefined local variable or method `assigns' for #<RSpec::Core::ExampleGroup::Nested_1:0x007fe2b88e2928>

2 个答案:

答案 0 :(得分:22)

assigns仅针对 controller 规范定义,并且通过rspec-rails gem完成。在RSpec中没有测试实例变量的通用机制,但您可以使用内核的instance_variable_get来访问您想要的任何实例变量。

所以在你的情况下,如果object是你有兴趣检查其实例变量的对象,你可以写:

expect(object.instance_variable_get(:@person)).to eql(user)

至于获得UserMailer实例的支持,我看不到任何方法可以做到这一点。查看https://github.com/rails/rails/blob/master/actionmailer/lib/action_mailer/base.rb内的method_missing定义,只要调用与实例方法同名的未定义类方法,就会创建一个新的邮件程序实例。但是该实例不会保存在我能看到的任何地方,只返回.message的值。这是目前在github上定义的相关代码:

班级方法:

  def respond_to?(method, include_private = false) #:nodoc:
    super || action_methods.include?(method.to_s)
  end

  def method_missing(method_name, *args) # :nodoc:
    if respond_to?(method_name)
      new(method_name, *args).message
    else
      super
    end
  end

实例方法:

attr_internal :message

# Instantiate a new mailer object. If +method_name+ is not +nil+, the mailer
# will be initialized according to the named method. If not, the mailer will
# remain uninitialized (useful when you only need to invoke the "receive"
# method, for instance).
def initialize(method_name=nil, *args)
  super()
  @_mail_was_called = false
  @_message = Mail.new
  process(method_name, *args) if method_name
end

def process(method_name, *args) #:nodoc:
  payload = {
    mailer: self.class.name,
    action: method_name
  }

  ActiveSupport::Notifications.instrument("process.action_mailer", payload) do
    lookup_context.skip_default_locale!

    super
    @_message = NullMail.new unless @_mail_was_called
  end
end

答案 1 :(得分:3)

除非Rails更改其实现,以便它实际上提供对ActionMailer(控制器)对象的访问权限而不仅仅是生成的Mail对象,否则我认为不可能进行测试。

正如Peter Alfvin指出的那样,问题是它在这里返回'消息':

new(method_name, *args).message

而不是像这样返回邮件程序(控制器):

new(method_name, *args)

rspec-rails列表中的这个post也可能有用:

  

似乎合理,但不太可能改变。这就是原因。 RSpec的护栏   提供rails提供的测试类的包装器。轨道   功能测试支持你上面提出的三个问题,但是rails   邮件测试是不同的。从   http://guides.rubyonrails.org/action_mailer_basics.html:“测试   邮件通常涉及两件事:一件是邮件排队,   另一个电子邮件是正确的。“

     

为了支持您希望在邮件程序规范中看到的内容,rspec-rails会   必须提供它自己的ExampleGroup(而不是包裹rails   class),它必须与rails的内部紧密绑定。一世   在rspec-rails-2中花了很大力气来限制与公众的联系   API,这有一个很大的回报:我们只有一个案例,其中一个   rails 3.x release需要释放一个rspec-rails(即有一个   破坏变化)。使用rails-2,几乎每个版本都破了   rspec-rails因为rspec-rails与内部结构相关(rspec-rails'   错误,而非铁路。)

     

如果你真的想看到这个变化,你需要改变它   在rails本身,此时rspec-rails将愉快地包装新的   并改进了MailerTestCase。