mandril :: API的object_double

时间:2016-11-15 16:33:17

标签: ruby-on-rails rspec mandrill

当使用 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.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的任何教程或明确示例。

2 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分:1)

尽管我从您的代码中可以看出,PostNotificationMailer.format_options(participant, post)无法知道其代码应该将send_template方法发送到您的double而不是预定义的MANDRILL.messages宾语。如果您在测试中调用Mandrill::API.new(ENV['MANDRILL_KEY']),即使您使用完全相同的代码定义MANDRILL,也会返回与MANDRILL完全不同的对象。因此,当邮件程序将方法发送到MANDRILL.messages时,您的双重内容就会被遗忘。

不幸的是,即使您的测试被重写为基于MANDRILL.messages的双倍,它仍然不会与邮件中的内容相同,因为邮件程序正在调用真实 MANDRILL.messages而不是你的双倍。我理解它的方式,对于大多数双打,你仍然必须使用依赖注入。也就是说,您的邮件程序必须进行设置,以便您可以设置一个参数,即&#34;执行邮件的对象,&#34;类似的事情(我正在做这件事)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的双打,所以我无法肯定地说。