简体   繁体   English

如何在rspec测试中跳过服务呼叫

[英]How do you skip a service call in a rspec test

Im writing a test for this service. 我正在为此服务编写测试。

  def run
    sort_offers(product).each do |product_code|
      ......
      offer.update(poduct_params)
      Importer::Partner.get_details(product_code).new
    end
  end

It's calling a service which in some cases will override the values that were saved when running offer.update(product_prams) . 它正在调用服务,在某些情况下,该服务将覆盖运行offer.update(product_prams)时保存的值。 How would I go about skipping the service call within my test? 如何在测试中跳过服务呼叫?

Here is the example of my test 这是我测试的例子

 context 'is valid' do
  .... .....
  before do
    Importer::ProductCodes(product).run
  end
  it ......
end

RSpec has a very capable stubbing and mocking library built in ( rspec mocks ). RSpec内置了一个功能非常强大的存根和模拟库( rspec嘲笑 )。

require 'spec_helper'

module Importer
  class Partner
    def self.get_details(product_code)
      "original return value"
    end
  end
end

class FooService
  def self.run
    Importer::Partner.get_details('bar')
  end
end

RSpec.describe FooService do
  let(:partner_double) { class_double("Importer::Partner") }

  before do
    stub_const("Importer::Partner", partner_double)
    allow(partner_double).to receive(:get_details).and_return 'our mocked value'
  end

  it "creates a double for the dependency" do
    expect(FooService.run).to eq 'our mocked value'
  end
end

class_double creates a double for the class and you can set the return values by using .expect and .allow and the mocking interface. class_double为该类创建一个double,您可以使用.expect.allow以及.expect接口来设置返回值。 This is quite useful since you can stub out the new or intialize methods to return a double or spy. 这非常有用,因为您可以将new方法或intialize方法存根以返回double或spy。

stub_constant will reset the constant to its previous value when the spec is done. 规范完成后, stub_constant会将常数重置为其先前的值。

That said you can avoid the use of stub_constant by using constructor injection in your services: 也就是说,可以通过在服务中使用构造函数注入来避免使用stub_constant

class PhotoImportService

  attr_accessor :client, :username

  def initialize(username, api_client: nil)
    @username = username
    @client = api_client || APIClient.new(ENV.fetch('API_KEY'))
  end

  def run
    client.get_photos(username)
  end
end

I would stub Importer::Partner.get_details to return a double that responds to new : 我将对Importer::Partner.get_details进行存根以返回一个对new作出响应的double

context 'is valid' do
  before do
    allow(Importer::Partner).to receive(:get_details).and_return(double(new: nil))
  end  

  # it ...
end

Depending on your needs you might want to add an expectation that the mock was called with the correct parameters and that new was actually called on the mock too: 根据您的需求,您可能希望增加使用正确参数调用该模拟并且实际上也对该模拟调用了new的期望:

context 'is valid' do
  let(:mock) { double(new: nil) }

  before do
    allow(Importer::Partner).to receive(:get_details).and_return(double(new: nil))
  end  

  it "calls the service" do
    an_instance.run

    expect(Importer::Partner).to have_received(:get_details).with(
      foo: 'bar' # the arguments you would expect
    )
    expect(mock).to have_received(:new)
  end
end

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM