52

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

现在在 rspec 中,当我尝试时出现错误(406)

get :index

我需要去做

get :index, :format => :json

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

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

4

11 回答 11

56
before :each do
  request.env["HTTP_ACCEPT"] = 'application/json'
end
于 2013-05-05T08:02:49.640 回答
25

把这个放在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
于 2013-01-31T10:50:32.573 回答
20

以下适用于我rspec 3

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

这设置HTTP_ACCEPT.

于 2015-01-15T16:57:05.807 回答
12

这是一个解决方案

  1. 适用于请求规格,
  2. 适用于 Rails 5,以及
  3. 不涉及 Rails 的私有 API(如process)。

这是 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.

这个解决方案的灵感来自knoopx's answer。他的解决方案不适用于请求规范,并且alias_method_chain已被弃用,取而代之的是Module#prepend.

于 2016-09-08T19:57:11.803 回答
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

于 2014-07-30T17:58:42.043 回答
6

也许您可以将第一个答案添加到 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 版本。

于 2014-10-31T14:29:35.953 回答
2

运行 Rails 5 和 Rspec 3.5 我必须设置标题来完成此操作。

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

Thi 与文档中的示例相匹配:

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
于 2017-02-12T21:49:56.240 回答
2

根据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
于 2019-08-19T19:50:39.620 回答
1

对于那些使用请求测试的人,我发现最简单的方法是覆盖#process方法ActionDispatch::Integration::Session并将默认as参数设置为:json如下所示:

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

ActionDispatch::Integration::Session.prepend(DefaultAsForProcess)
于 2017-09-09T08:54:13.357 回答
0

不确定这是否适用于这种特定情况。但我特别需要的是能够将params哈希传递给该post方法。大多数解决方案似乎都是 forrspec 3和 up,并提到添加第三个参数,如下所示:

post '/post_path', params: params_hash, :format => 'json'

(或类似的,:format => 'json'位有所不同)

但这些都没有奏效。控制器会收到一个类似于: 的散列{params: => { ... }},带有不需要的params:密钥。

有效的(使用 rails 3 和 rspec 2)是:

post '/post_path', params_hash.merge({:format => 'json'})

另请查看此相关帖子,我从中获得了解决方案:使用 Rspec,如何在 Rails 3.0.11 中测试我的控制器的 JSON 格式?

于 2021-12-17T17:35:15.223 回答
-3

为什么 RSpec 的方法“get”、“post”、“put”、“delete”不能在 gem(或 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
于 2012-07-20T17:06:57.477 回答