Rails 4 + Rspec:使用REST API的父控制器进行自定义身份验证的DRY测试?

时间:2015-03-27 21:48:08

标签: ruby-on-rails ruby rest authentication rspec

我正在构建REST API并且拥有Api::ApiController,它是资源控制器的父级,并保存身份验证代码,以便多个资源可以继承身份验证功能。该控制器本身没有任何操作,那么如何使用rspec进行测试呢?我无法向其发送任何GET请求,并且我希望将测试保持为DRY作为实际实施。

class Api::V1::ApiController < ActionController::Base
  before_action :authenticate

  protected
  def authenticate
    request_key = request.headers['X-Api-Key']
    master_key  = Rails.application.secrets.something['api_key']

    unless secure_compare(request_key, master_key)
      head status: 401
      return false
    end
  end

  # extracted from ActiveSupport::MessageVerifier#secure_compare
  def secure_compare(a, b)
    return false unless a.bytesize == b.bytesize

    l = a.unpack "C#{a.bytesize}"

    res = 0
    b.each_byte { |byte| res |= byte ^ l.shift }
    res == 0
  end
end


# app/controllers/api/v1/resources_controller.rb
class Api::V1::ResourcesController < Api::ApiController
  def index
    ..
  end

  # etc etc
end

# spec/controllers/api/v1/resources_controller_spec.rb
  context 'Api::ApiController#authenticate' do 
    describe 'bad key' do
      before { request.headers['X-API-KEY'] = 'xyz' }

      it 'prevents unauthorized access' do 
        get 'index', format: :json
        expect(response.status).to eq 401
      end
    end

    describe 'good key' do 
      before { request.headers['X-API-KEY'] = Rails.application.secrets.something['api_key'] }

      it 'grants access' do 
        get 'index', format: :json
        expect(response.status).to eq 200
      end
    end
  end

我在上面的#authenticate方法的资源控制器规范文件中进行了测试,但是将它们放在那里感觉不对,因为a)该方法是{{1}的一部分不是Api::ApiController,而且因为理想情况下我需要在每个资源的控制器中验证身份验证。

我是否需要添加操作(类似Api::ResourceControllerindex)以及此控制器进行身份验证测试所需的路径?

1 个答案:

答案 0 :(得分:2)

您可以设置一个继承ApiController的抽象控制器(请参阅Test a base (abstract) controller),但我认为您可能会采用错误的方式。

您可以将您的身份验证逻辑放在一个关注点或帮助程序中,而不是子类化。

优点是您可以直接在测试中调用方法,而不是尝试设置请求。控制器规范在auth方面可能很挑剔,因为它们没有会话和实际请求的环境。

然后我会向第一个控制器添加一个功能测试,该控制器实现身份验证以测试集成。