【问题标题】:Set Rspec default GET request format to JSON将 Rspec 默认 GET 请求格式设置为 JSON
【发布时间】:2012-06-16 21:08:07
【问题描述】:

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

现在在 rspec 中,我尝试时遇到错误 (406)

get :index

我需要做的

get :index, :format => :json

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

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

【问题讨论】:

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


    【解决方案1】:
    before :each do
      request.env["HTTP_ACCEPT"] = 'application/json'
    end
    

    【讨论】:

    • @Erik 你有其他建议吗,因为它在 Rspec 3 中不起作用?
    • 新的 Rspec 3 语法将非常受欢迎
    • 您在哪里看到“请求”变量?
    • RSpec3 世界中的情况可能发生了变化。
    • 这适用于我在 Rspec 3+,request.accept = "application/json"
    【解决方案2】:

    把这个放到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
    

    【讨论】:

    • 这太棒了!感谢分享。只是一个提示。由于在 #process_with_default_params 中使用了 #merge,您仍然可以将规范中的格式“覆盖”为 json 以外的其他格式 - 例如 html。
    • 也许操作后的所有内容都应该默认为非强制性的?
    • 酷!非常感谢,这对我真的很有用:)
    • 这实际上对我不起作用,但这个类似的解决方案确实有效:snip2code.com/Snippet/13535/…
    • 我得到了一个未定义的方法 alias_method_chain -- 有解决办法吗?
    【解决方案3】:

    以下内容适用于 rspec 3

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

    这设置HTTP_ACCEPT

    【讨论】:

    • 这对我来说适用于 Rails 5.0.1 + RSpec 3.5.2 + JBuilder 2.6.0。
    【解决方案4】:

    这是一个解决方案

    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

    【讨论】:

      【解决方案5】:

      在 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

      【讨论】:

      • 我认为 config.render_views 足以获取 JSON 视图响应。
      【解决方案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 版本。

      【讨论】:

      • 如果你把它改成 config.before(:each, type: :controller),那么它就不会达到模型规格。
      【解决方案7】:

      运行 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
      

      【讨论】:

        【解决方案8】:

        根据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】:

          对于那些使用请求测试的人,我发现最简单的方法是覆盖ActionDispatch::Integration::Session 中的#process 方法并将默认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)
          

          【讨论】:

          • 当有其他可行的解决方案时,我觉得猴子修补课程是错误的方法。
          • @AndrewKS 可能这不是最好的解决方案,但我发现它最适用于我的案例,因为其他人对不同类型的请求有一些问题,例如 get params 应该是查询字符串,post params 应该在正文等设置标头没有考虑这种情况,但在请求中设置“as”只会以最预期的方式发出请求,并且它依赖于内置行为
          【解决方案10】:

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

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

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

          但这些都不起作用。控制器会收到一个类似于:{params: => { ... }} 的散列,带有不需要的 params: 键。

          起作用的是(使用 rails 3 和 rspec 2):

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

          还可以查看这个相关的帖子,我的解决方案来自:Using Rspec, how do I test the JSON format of my controller in Rails 3.0.11?

          【讨论】:

            【解决方案11】:

            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
            

            【讨论】:

              猜你喜欢
              • 2021-10-10
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2017-11-21
              • 2017-10-15
              • 1970-01-01
              • 2014-02-22
              • 2017-12-02
              相关资源
              最近更新 更多