如何在Rails 5 API应用程序中使用RSpec测试控制器关注方法

时间:2019-02-27 10:18:45

标签: rspec-rails

我有以下控制器关注模块:

#controllers/concerns/response.rb

module Response
  extend ActiveSupport::Concern

  def json_response(object, status = :ok, opts = {})
    response = {json: object, status: status}.merge(opts)
    render response
  end
...
end

ApplicationController包括以下内容:

class ApplicationController < ActionController::API
  include Response
...
end

如何测试上述关注方法?它应该是哪种RSpec测试(控制器,请求)?

我尝试如下定义shared_examples

#spec/shared/json_response.rb

require 'rails_helper'

RSpec.shared_examples 'JSON Responsive controller' do |controller_class|
  let(:controller_class) { controller_class }

  it 'render JSON response' do
    expect(controller_class).to respond_to(:json_response)
  end
end

并在控制器规范中使用它:

#spec/controllers/concerns/fake_controller.rb

require 'rails_helper'

class FakeController < ApplicationController
end

RSpec.describe FakeController, type: :controller do
  it_behaves_like 'JSON Responsive controller', FakeController
end

但是失败了:

Failures:

  1) FakeController behaves like JSON Responsive controller render JSON response
     Failure/Error: expect(controller_class).to respond_to(:json_response)
       expected FakeController to respond to :json_response
     Shared Example Group: "JSON Responsive controller" called from ./spec/controllers/concerns/fake_controller_spec.rb:7
     # ./spec/shared/json_response.rb:7:in `block (2 levels) in <main>'

Finished in 0.23535 seconds (files took 1.11 seconds to load)
1 example, 1 failure

我想念什么?

1 个答案:

答案 0 :(得分:0)

这是我要使其工作的解决方案。

  1. spec/controller/fake_controller_spec.rb中创建控制器规范,如下所示:
require 'rails_helper'

class FakeController < ApplicationController

  def render(*args)
    args.first
  end
end

RSpec.describe FakeController, type: :controller do
  it_should_behave_like "JSON Responsive controller", FakeController
end

我必须重写render(*args)方法才能从render模块关注内部调用Response

  1. spec/shared/json_response.rb中创建一个shared_examples规范:
require 'rails_helper'

RSpec.shared_examples 'JSON Responsive controller' do |including_controller|
  let(:instance) { including_controller.new }

  it 'should respond to #json_response' do
    expect(instance).to respond_to(:json_response)
  end

  it 'should respond #respond_with_errors' do
    expect(instance).to respond_to(:respond_with_errors)
  end

  it 'should respond to #paginated_response_status' do
    expect(instance).to respond_to(:paginated_response_status)
  end

  context '#paginated_response_status' do
    it 'returns 200 if collection is not paginated' do
      expect(instance.paginated_response_status([1])).to eq :ok
    end

    it 'returns 206 if collection is paginated' do
      collection = (1..35).to_a
      expect(instance.paginated_response_status(collection)).to eq :partial_content
    end
  end

  context '#respond_with_errors' do
    it 'returns :unprocessable_entity status' do
      model = double(:model)
      errors = double(:errors, messages: {})
      allow(model).to receive(:errors).and_return(errors)
      response = instance.respond_with_errors(model)
      expect(response[:status]).to eq :unprocessable_entity
    end
  end

  context '#json_response' do
    it 'returns JSON with default :ok status' do
      model = double(:model)
      response = instance.json_response(model)
      expect(response[:status]).to eq :ok
    end

    it 'returns JSON with the specified status' do
      model = double(:model)
      response = instance.json_response(model, :partial_content)
      expect(response[:status]).to eq :partial_content
    end
  end
end

请注意,要使用shared文件夹中定义的共享示例,必须将以下内容添加到rails_helper.rb文件中:

Dir[Rails.root.join('spec/shared/**/*.rb')].each  { |f| require f }
...
RSpec.configure do |config|
..
end
  1. 最后,这是controllers/concerns/response.rb中定义的要测试的代码:
module Response
  extend ActiveSupport::Concern

  def json_response(object, status = :ok, opts = {})
    response = {json: object, status: status}.merge(opts)
    render response
  end

  def respond_with_errors(object)
    render json: { errors: ErrorSerializer.serialize(object) }, status: :unprocessable_entity
  end

  def paginated_response_status(collection)
    collection.size > WillPaginate.per_page ? :partial_content : :ok
  end
end

ErrorSerializer只是另一个模块,该模块创建JSON以在发生错误时返回:

#controllers/concerns/error_serializer.rb

module ErrorSerializer
  extend ActiveSupport::Concern

  def self.serialize(object)
    object.errors.messages.map do |field, errors|
      errors.map do |error_message|
        {
          status: 422,
          title: 'Invalid attribute',
          source: { pointer: "/data/attributes/#{field}" },
          detail: error_message
        }
      end
    end.flatten
  end
end

希望这会有所帮助。