【问题标题】:object_double for Mandril::APIMandrill::API 的 object_double
【发布时间】:2017-03-29 15:26:03
【问题描述】:

当使用 ma​​ndril-api 时,Rails Action Mailer 会被绕过,所以不可能做这样的事情

it 'sends an email' do
   expect { subject.send_instructions }
     .to change { ActionMailer::Base.deliveries.count }.by(1)
end

我正在尝试使用 object_double 来测试我的邮件。我要测试的正是发送到 API 的参数(通过选项哈希)。

到目前为止,我这里有 Mandrill 代码

  MANDRILL.messages.send_template( options[:template], [], message) unless Rails.env.staging?

MANDRILL 只是与 API 的连接,如下所述。

describe 'verify content' do
  it 'uses the correct template' do
    api = object_double(Mandrill::API.new(ENV['MANDRILL_KEY']).messages)
    allow(api).to receive(:send_template)
    PostNotificationMailer.format_options(participant, post)
    expect(api).to have_received(:send_template)
    #expect(options[:template]).to eq('crowdai_standard_template')
  end
end

我希望能够在此处检查传递给 Mandrill API 的所有参数。我可以模拟 messages 方法,但不能模拟 messages.send_template

1) PostNotificationMailer verify content uses the correct template
   Failure/Error: expect(api).to have_received(:send_template)

   (ObjectDouble(#<Mandrill::Messages:0x007f8debd4f348 @master=#<Mandrill::API:0x007f8debd4faf0 @host="https://mandrillapp.com", @path="/api/1.0/", @session=#<Excon::Connection:7f8debd4f758 @data={:chunk_size=>1048576, :ciphers=>"HIGH:!SSLv2:!aNULL:!eNULL:!3DES", :connect_timeout=>60, :debug_request=>false, :debug_response=>false, :headers=>{"User-Agent"=>"excon/0.51.0"}, :idempotent=>false, :instrumentor_name=>"excon", :middlewares=>[Excon::Middleware::Hijack, Excon::Middleware::ResponseParser, Excon::Middleware::Expects, Excon::Middleware::Idempotent, Excon::Middleware::Instrumentor, Excon::Middleware::Mock], :mock=>false, :nonblock=>true, :omit_default_port=>false, :persistent=>false, :read_timeout=>60, :retry_limit=>4, :ssl_verify_peer=>true, :ssl_uri_schemes=>["https"], :stubs=>:global, :tcp_nodelay=>false, :thread_safe_sockets=>true, :uri_parser=>URI, :versions=>"excon/0.51.0 (x86_64-darwin15) ruby/2.3.1", :write_timeout=>60, :host=>"mandrillapp.com", :hostname=>"mandrillapp.com", :path=>"", :port=>443, :query=>nil, :scheme=>"https"} @socket_key="https://mandrillapp.com:443">, @debug=false, @apikey="redacted">>) (anonymous)).send_template(*(any args))
       expected: 1 time with any arguments
       received: 0 times with any arguments
 # ./spec/mailers/post_notification_mailer_spec.rb:14:in `block (3 levels) in <top (required)>'

** 编辑 **

有一个 gem MandrillMailer 解决了针对 Mandril API 进行测试的问题,但是它的构建被破坏了,而且它似乎也在内部重新构建 API。

How do I test mandrill api with rspec

我找不到任何关于如何使用 object_double 的教程或清晰的示例。

【问题讨论】:

  • 这实际上有两部分:如何在测试期间拦截发送到 Mandrill API 的选项哈希,以及如何在 Rspec 中使用 object_double
  • 请在主题中粘贴subject.send_instructions内容方法

标签: ruby-on-rails rspec mandrill


【解决方案1】:

您是否考虑过使用 VCR gem (https://github.com/vcr/vcr) 将 API 调用对 mandrill 的响应记录到夹具中?记录请求后,您可以断言响应中的值以验证预期数据是否已通过。

【讨论】:

  • 嗨@Joey 我有VCR gem。我想学习如何使用 object_doubles,因为它们的例子很少。我还想捕获我的应用程序发送到 API 的内容,而不调用它。有一个 gem MandrillMailer 但它似乎在内部重建了 API。 stackoverflow.com/questions/19508030/…
  • 使用 VCR,您只需在第一次运行测试时发出 API 请求。每次连续重播cassette 并且不发出远程API 请求。考虑到它在内部重建 API,使用 mandrillmailer gem 作为学习 object_doubles 的机会似乎是一个糟糕的用例。由于 API 未公开,您将在 gem 的内部 API 更改时遇到不一致。
【解决方案2】:

据我所知,PostNotificationMailer.format_options(participant, post) 无法知道它的代码应该将send_template 方法发送给您的替身,而不是预定义的MANDRILL.messages 对象。如果您在测试中调用Mandrill::API.new(ENV['MANDRILL_KEY']),即使您使用完全相同的代码定义MANDRILL,它也会返回与MANDRILL 完全不同的对象。因此,当邮件程序将方法发送到MANDRILL.messages 时,您的替身是无视的。

不幸的是,即使您的测试被重写为基于 MANDRILL.messages 的双精度,它仍然不会是与邮件中的对象相同的对象,因为邮件正在调用 real MANDRILL.messages 而不是你的替身。按照我的理解,对于大多数双打来说,你仍然必须使用依赖注入。也就是说,必须设置您的邮件程序,以便您可以设置一个参数,该参数将是“发送邮件的对象”,类似于(我正在编造这个)PostNotificationMailer.set_api(some_object)。在生产中,它将是PostNotificationMailer.set_api(MANDRILL),而在您的测试中,它将是PostNotificationMailer.set_api(api)。可能这比它的价值更麻烦。

object_double documentation似乎证实了这一点,其中测试包括:

user = object_double(User.new, :save => true)
expect(save_user(user)).to eq("saved!")

如您所见,user 对象作为参数传递到我们尝试测试的方法中,以便在 double 而不是其他对象上调用方法。

RSpec 似乎确实具有在常量上使用对象双精度的有趣能力,因此您不必使用依赖注入。但是,基于the relevant documentation,看起来您必须将对象名称作为字符串(不是实际的对象引用)传递,然后您必须在双精度上调用as_stubbed_const

logger = object_double("MyApp::LOGGER", :info => nil).as_stubbed_const
Email.send_to('hello@foo.com')
expect(logger).to have_received(:info).with("Sent to hello@foo.com")

因此,如果您的应用程序为 API 的 messages 对象定义了一个常量,然后将其名称作为字符串传递并调用 as_stubbed_const,它可能会起作用。我没有尝试过像这样使用 RSpec 的双打,所以我不能肯定。

【讨论】:

    猜你喜欢
    • 2015-12-18
    • 2013-02-12
    • 2013-07-20
    • 2014-09-13
    • 2016-05-05
    • 1970-01-01
    • 2014-08-26
    • 2014-10-10
    • 2014-07-15
    相关资源
    最近更新 更多