将Rspec默认GET请求格式设置为JSON

时间:2012-06-13 20:30:15

标签: ruby-on-rails rspec functional-testing

我正在使用Rspec为我的控制器进行功能测试。我已将路由器中的默认响应格式设置为JSON,因此每个没有后缀的请求都将返回JSON。

现在在rspec中,当我尝试

时出现错误(406)
get :index

我需要做

get :index, :format => :json

现在因为我主要使用我的API支持JSON,所以必须为每个请求指定JSON格式是非常多余的。

我可以以某种方式将其设置为我的所有GET请求的默认值吗? (或所有请求)

10 个答案:

答案 0 :(得分:48)

before :each do
  request.env["HTTP_ACCEPT"] = 'application/json'
end

答案 1 :(得分:24)

将其放入spec/support

require 'active_support/concern'

module DefaultParams
  extend ActiveSupport::Concern

  def process_with_default_params(action, parameters, session, flash, method)
    process_without_default_params(action, default_params.merge(parameters || {}), session, flash, method)
  end

  included do
    let(:default_params) { {} }
    alias_method_chain :process, :default_params
  end
end

RSpec.configure do |config|
  config.include(DefaultParams, :type => :controller)
end

然后只需覆盖default_params

describe FooController do
    let(:default_params) { {format: :json} }
    ...
end

答案 2 :(得分:14)

以下适用于我的 rspec 3

before :each do
  request.headers["accept"] = 'application/json'
end

这会设置HTTP_ACCEPT

答案 3 :(得分:7)

在RSpec 3中,您需要将JSON测试作为请求规范才能呈现视图。这是我使用的:

# spec/requests/companies_spec.rb
require 'rails_helper'

RSpec.describe "Companies", :type => :request do
  let(:valid_session) { {} }

  describe "JSON" do
    it "serves multiple companies as JSON" do
      FactoryGirl.create_list(:company, 3)
      get 'companies', { :format => :json }, valid_session
      expect(response.status).to be(200)
      expect(JSON.parse(response.body).length).to eq(3) 
    end

    it "serves JSON with correct name field" do
      company = FactoryGirl.create(:company, name: "Jane Doe")
      get 'companies/' + company.to_param, { :format => :json }, valid_session
      expect(response.status).to be(200)
      expect(JSON.parse(response.body)['name']).to eq("Jane Doe")
    end
  end
end

至于在所有测试中设置格式,我喜欢其他答案的方法:https://stackoverflow.com/a/14623960/1935918

答案 4 :(得分:7)

这是

的解决方案
  1. 适用于请求规范,
  2. 适用于Rails 5和
  3. 不涉及Rails的私有API(如process)。
  4. 这是RSpec配置:

    module DefaultFormat
      extend ActiveSupport::Concern
    
      included do
        let(:default_format) { 'application/json' }
        prepend RequestHelpersCustomized
      end
    
      module RequestHelpersCustomized
        l = lambda do |path, **kwarg|
          kwarg[:headers] = {accept: default_format}.merge(kwarg[:headers] || {})
          super(path, **kwarg)
        end
        %w(get post patch put delete).each do |method|
          define_method(method, l)
        end
      end
    end
    
    RSpec.configure do |config|
      config.include DefaultFormat, type: :request
    end
    

    验证

    describe 'the response format', type: :request do
      it 'can be overridden in request' do
        get some_path, headers: {accept: 'text/plain'}
        expect(response.content_type).to eq('text/plain')
      end
    
      context 'with default format set as HTML' do
        let(:default_format) { 'text/html' }
    
        it 'is HTML in the context' do
          get some_path
          expect(response.content_type).to eq('text/html')
        end
      end
    end
    

    FWIW,可以放置RSpec配置:

    1. 直接在spec/spec_helper.rb。这不是建议;即使在lib/中测试库方法,也会加载该文件。

    2. 直接在spec/rails_helper.rb

    3. (我最喜欢的)在spec/support/default_format.rb中,并在spec/rails_helper.rb中明确加载

      require 'support/default_format'
      
    4. spec/support中,由

      加载
      Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
      

      加载spec/support

    5. 中的所有文件

      此解决方案的灵感来自knoopx's answer。他的解决方案不适用于请求规范,alias_method_chain已被弃用,转而使用Module#prepend

答案 5 :(得分:5)

也许您可以将第一个答案添加到spec / spec_helper或spec / rails_helper中:

config.before(:each) do
  request.env["HTTP_ACCEPT"] = 'application/json' if defined? request
end

如果在模型测试(或任何不存在的请求方法上下文)中,此代码只是忽略。 它适用于rspec 3.1.7和rails 4.1.0 一般来说,它应该适用于所有rails 4版本。

答案 6 :(得分:2)

运行Rails 5和Rspec 3.5我必须设置标头才能完成此任务。

post '/users', {'body' => 'params'}, {'ACCEPT' => 'application/json'}

这与docs中的示例匹配:

require "rails_helper"

RSpec.describe "Widget management", :type => :request do
  it "creates a Widget" do
    headers = {
      "ACCEPT" => "application/json",     # This is what Rails 4 accepts
      "HTTP_ACCEPT" => "application/json" # This is what Rails 3 accepts
    }
    post "/widgets", { :widget => {:name => "My Widget"} }, headers

    expect(response.content_type).to eq("application/json")
    expect(response).to have_http_status(:created)
  end
end

答案 7 :(得分:1)

对于那些使用请求测试的人来说,我找到的最简单方法是覆盖#process中的ActionDispatch::Integration::Session方法,并将默认as参数设置为:json,如下所示:< / p>

module DefaultAsForProcess
  def process(method, path, params: nil, headers: nil, env: nil, xhr: false, as: :json)
    super
  end
end

ActionDispatch::Integration::Session.prepend(DefaultAsForProcess)

答案 8 :(得分:0)

对于Rspec docs,受支持的方法是通过标头:

require "rails_helper"

RSpec.describe "Widget management", :type => :request do

  it "creates a Widget" do
    headers = {
      "ACCEPT" => "application/json",      # This is what Rails 4 and 5 accepts
      "HTTP_ACCEPT" => "application/json", # This is what Rails 3 accepts
    }
    post "/widgets", :params => { :widget => {:name => "My Widget"} }, :headers => headers

    expect(response.content_type).to eq("application/json")
    expect(response).to have_http_status(:created)
  end

end

答案 9 :(得分:-3)

Why don't RSpec's methods, "get", "post", "put", "delete" work in a controller spec in a gem (or outside Rails)?

基于这个问题,您可以尝试从https://github.com/rails/rails/blob/32395899d7c97f69b508b7d7f9b7711f28586679/actionpack/lib/action_controller/test_case.rb重新定义ActionController :: TestCase中的process()。

这是我的解决方法。

describe FooController do
    let(:defaults) { {format: :json} }

    context 'GET index' do
        let(:params) { defaults }
        before :each do
            get :index, params
        end

        # ...
    end

    context 'POST create' do
        let(:params) { defaults.merge({ name: 'bar' }) }
        before :each do
            post :create, params
        end

        # ...
    end
end