首页 > 解决方案 > 使用 RSPEC 和 ROR 在 rails 控制器中存根 api 调用

问题描述

我有以下导轨控制器

class FoodsController < ApplicationController
  before_action :set_food, only: [:show, :update, :destroy]


  # GET /foods/1
  def show
   
    #render json: @food
    render json: 
    {
      "barcode": @food.barcode,
      "product": @food.product
    }
  
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_food
      #@food = Food.find(params[:id])
      
      code = params[:id]
      product = Openfoodfacts::Product.get(code, locale: 'fr')
      @food = Food.create_with(product: product.product_name).find_or_create_by(barcode: code)
    end

    # Only allow a list of trusted parameters through.
    def food_params
      params.require(:food).permit(:barcode, :product)
    end
end

我有以下 rspec 测试

require 'rails_helper'

RSpec.describe FoodsController, type: :request do
  include_context 'openfood_context'

  let(:headers) { { Authorization: "Token #{TokenAuth::API_TOKEN}" } }
  let(:response_body) { JSON.parse(response.body) }
  let(:given_product) { response_body['product'] }
  let(:given_barcode) { response_body['barcode'] }

 
  before do
    get food_path(id: barcode), headers: headers
    expect(response).to have_http_status(:ok)
    
  end

  it 'returns the product name' do
    
    expect(given_product).to eq product
  end

  it 'returns the barcode' do
    expect(given_barcode).to eq barcode
  end
end

这些页面有效,当我点击 foods/099482476885 路线时,我得到这张图片中的输出,在此处输入图像描述。但是当我运行规范时,我得到下面的错误

1) FoodsController returns the product name
     Failure/Error: product = Openfoodfacts::Product.get(code, locale: 'fr')
     
     WebMock::NetConnectNotAllowedError:
       Real HTTP connections are disabled. Unregistered request: GET https://fr.openfoodfacts.org/api/v0/produit/049000061017.json with headers {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'User-Agent'=>'Ruby'}
     
       You can stub this request with the following snippet:
     
       stub_request(:get, "https://fr.openfoodfacts.org/api/v0/produit/049000061017.json").
         with(
           headers: {
          'Accept'=>'*/*',
          'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
          'User-Agent'=>'Ruby'
           }).
         to_return(status: 200, body: "", headers: {})
     
       ============================================================
     # /home/ubuntu/.rvm/gems/ruby-2.6.3/gems/webmock-3.13.0/lib/webmock/http_lib_adapters/net_http.rb:114:in `request'
     # /home/ubuntu/.rvm/gems/ruby-2.6.3/gems/webmock-3.13.0/lib/webmock/http_lib_adapters/net_http.rb:123:in `start_without_connect'
     # /home/ubuntu/.rvm/gems/ruby-2.6.3/gems/webmock-3.13.0/lib/webmock/http_lib_adapters/net_http.rb:150:in `start'
     # /home/ubuntu/.rvm/gems/ruby-2.6.3/gems/openfoodfacts-0.6.0/lib/openfoodfacts/product.rb:25:in `get'
     # ./app/controllers/foods_controller.rb:23:in `set_food'
     # /home/ubuntu/.rvm/gems/ruby-2.6.3/gems/rack-2.2.3/lib/rack/etag.rb:27:in `call'
     # /home/ubuntu/.rvm/gems/ruby-2.6.3/gems/rack-2.2.3/lib/rack/conditional_get.rb:27:in `call'
     # /home/ubuntu/.rvm/gems/ruby-2.6.3/gems/rack-2.2.3/lib/rack/head.rb:12:in `call'
     # /home/ubuntu/.rvm/gems/ruby-2.6.3/gems/railties-6.1.3.2/lib/rails/rack/logger.rb:37:in `call_app'
     # /home/ubuntu/.rvm/gems/ruby-2.6.3/gems/railties-6.1.3.2/lib/rails/rack/logger.rb:26:in `block in call'
     # /home/ubuntu/.rvm/gems/ruby-2.6.3/gems/railties-6.1.3.2/lib/rails/rack/logger.rb:26:in `call'
     # /home/ubuntu/.rvm/gems/ruby-2.6.3/gems/rack-2.2.3/lib/rack/runtime.rb:22:in `call'
     # /home/ubuntu/.rvm/gems/ruby-2.6.3/gems/rack-2.2.3/lib/rack/sendfile.rb:110:in `call'
     # /home/ubuntu/.rvm/gems/ruby-2.6.3/gems/railties-6.1.3.2/lib/rails/engine.rb:539:in `call'
     # /home/ubuntu/.rvm/gems/ruby-2.6.3/gems/rack-test-1.1.0/lib/rack/mock_session.rb:29:in `request'
     # /home/ubuntu/.rvm/gems/ruby-2.6.3/gems/rack-test-1.1.0/lib/rack/test.rb:266:in `process_request'
     # /home/ubuntu/.rvm/gems/ruby-2.6.3/gems/rack-test-1.1.0/lib/rack/test.rb:119:in `request'
     # ./spec/requests/foods_controller_spec.rb:13:in `block (2 levels) in <top (required)>'
     # /home/ubuntu/.rvm/gems/ruby-2.6.3/gems/webmock-3.13.0/lib/webmock/rspec.rb:37:in `block (2 levels) in <top (required)>'

然后我尝试将以下存根添加到 spec/spec_helper.rb 但得到与图片中的错误类似的错误。我该如何解决这个规范问题?

 config.before(:each) do
    stub_request(:get, /api.openfoodfacts.org/).
      with(headers: {
        'Accept'=>'*/*', 
        'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
        'User-Agent'=>'Ruby'}).
      to_return(status: 200, body: "stubbed response", headers: {})
  end

标签: ruby-on-railsrubyapirspecrspec-rails

解决方案


您正在将请求存根,api.openfoodfacts.org但您分享了一个错误,抱怨向fr.openfoodfacts.org.

Webmock 将阻止您规范中的所有外部请求(这是设计使然),因此当您将请求存根时foo,请特别注意错误消息是否更改为Unregistered request: bar. 你需要把它们全部存根,

模拟Openfoodfacts::Product自身:

allow(Openfoodfacts::Product).to receive(:get).and_return(...)

(这将取消禁用此类的“正常”行为,并且任何调用都会返回您告诉它返回的内容。

第三种选择可能是使用VCR gem,它可以记录请求和响应,然后为您模拟它们。

每个解决方案都有其优点和缺点,(我认为)超出了这个问题的范围。


推荐阅读