Ruby-on-rails 如何使用 rspec 测试我的邮件程序中是否设置了实例变量?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/21080407/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
how do I test that an instance variable is set in my my mailer with rspec?
提问by pixelearth
How do I test that a certain instance variable is set in my my mailer with rspec? assigns is coming back undefined..
如何使用 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
The error returned is:
返回的错误是:
undefined local variable or method `assigns' for #<RSpec::Core::ExampleGroup::Nested_1:0x007fe2b88e2928>
回答by Peter Alfvin
assignsis only defined for controllerspecs and that's done via the rspec-rails gem. There is no general mechanism to test instance variables in RSpec, but you can use Kernel's instance_variable_getto access any instance variable you want.
assigns仅针对控制器规格定义,这是通过 rspec-rails gem 完成的。在 RSpec 中没有测试实例变量的通用机制,但是您可以使用内核instance_variable_get来访问您想要的任何实例变量。
So in your case, if objectwere the object whose instance variable you were interested in checking, you could write:
因此,在您的情况下,如果object您有兴趣检查其实例变量的对象,您可以编写:
expect(object.instance_variable_get(:@person)).to eql(user)
As for getting ahold of the UserMailerinstance, I can't see any way to do that. Looking at the method_missingdefinition inside https://github.com/rails/rails/blob/master/actionmailer/lib/action_mailer/base.rb, a new mailer instance will be created whenever an undefined class method is called with the same name as an instance method. But that instance isn't saved anywhere that I can see and only the value of .messageis returned. Here is the relevant code as currently defined on github:
至于获取UserMailer实例,我看不出有任何方法可以做到这一点。查看https://github.com/rails/rails/blob/master/actionmailer/lib/action_mailer/base.rb中的method_missing定义,每当使用与一个实例方法。但是该实例不会保存在我能看到的任何地方,并且只返回的值。这是目前在 github 上定义的相关代码:.message
Class methods:
类方法:
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
Instance methods:
实例方法:
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
回答by Tyler Rick
I don't think this is possible to test unless Rails changes its implementation so that it actually provides access to the ActionMailer (controller) object and not just the Mail object that is generated.
我认为这是不可能测试的,除非 Rails 更改其实现,以便它实际上提供对 ActionMailer(控制器)对象的访问,而不仅仅是生成的 Mail 对象。
As Peter Alfvin pointed out, the problem is that it returns the 'message' here:
正如 Peter Alfvin 指出的那样,问题在于它在这里返回“消息”:
new(method_name, *args).message
instead of just returning the mailer (controller) like this:
而不是像这样返回邮件程序(控制器):
new(method_name, *args)
This poston the rspec-rails list might also be helpful:
rspec-rails 列表上的这篇文章也可能有帮助:
Seems reasonable, but unlikely to change. Here's why. rspec-rails provides wrappers around test classes provided by rails. Rails functional tests support the three questions you pose above, but rails mailer tests are different. From http://guides.rubyonrails.org/action_mailer_basics.html: "Testing mailers normally involves two things: One is that the mail was queued, and the other one that the email is correct."
To support what you'd like to see in mailer specs, rspec-rails would have to provide it's own ExampleGroup (rather than wrap the rails class), which would have to be tightly bound to rails' internals. I took great pains in rspec-rails-2 to constrain coupling to public APIs, and this has had a big payoff: we've only had one case where a rails 3.x release required a release of rspec-rails (i.e. there was a breaking change). With rails-2, pretty much every release broke rspec-rails because rspec-rails was tied to internals (rspec-rails' fault, not rails).
If you really want to see this change, you'll need to get it changed in rails itself, at which point rspec-rails will happily wrap the new and improved MailerTestCase.
看似合理,但不太可能改变。这是为什么。rspec-rails 提供了围绕 rails 提供的测试类的包装器。Rails 功能测试支持您在上面提出的三个问题,但 Rails 邮件程序测试不同。来自 http://guides.rubyonrails.org/action_mailer_basics.html:“测试邮件程序通常涉及两件事:一是邮件已排队,另一是电子邮件是正确的。”
为了支持您希望在邮件程序规范中看到的内容,rspec-rails 必须提供它自己的 ExampleGroup(而不是包装 rails 类),它必须与 rails 的内部结构紧密绑定。我在 rspec-rails-2 中煞费苦心地限制与公共 API 的耦合,这带来了巨大的回报:我们只有一种情况,rails 3.x 版本需要发布 rspec-rails(即有一个突破性的变化)。使用 rails-2,几乎每个版本都会破坏 rspec-rails,因为 rspec-rails 与内部结构相关(rspec-rails 的错误,而不是 rails)。
如果你真的想看到这个变化,你需要在 rails 本身中改变它,此时 rspec-rails 会很高兴地包装新的和改进的 MailerTestCase。

