object_double 对于 Mandril::API

object_double for Mandril::API

当使用 mandril-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 的教程或清晰的示例。

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

据我所知,从您的代码中可以看出,PostNotificationMailer.format_options(participant, post) 无法知道它的代码应该将 send_template 方法发送给您的 double 而不是预定义的 [=14] =] 对象。如果您在测试中调用 Mandrill::API.new(ENV['MANDRILL_KEY']),那 returns 是一个与 MANDRILL 完全不同的对象,即使您使用完全相同的代码定义了 MANDRILL。因此,当邮件程序将方法发送到 MANDRILL.messages 时,您的替身将被遗忘。

不幸的是,即使您的测试被重写为基于 MANDRILL.messages 生成 double,它仍然不会与您的邮件程序中的对象相同,因为邮件程序正在调用 real MANDRILL.messages 而不是你的替身。按照我的理解,对于大多数双打,您仍然必须使用依赖注入。也就是说,必须设置您的邮件程序,以便您可以设置一个 "the object that does the mailing," 类似(我正在编造)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 double:

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 的双打,所以我不能肯定地说。