我正在构建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::ResourceController
或index
)以及此控制器进行身份验证测试所需的路径?
答案 0 :(得分:2)
您可以设置一个继承ApiController的抽象控制器(请参阅Test a base (abstract) controller),但我认为您可能会采用错误的方式。
您可以将您的身份验证逻辑放在一个关注点或帮助程序中,而不是子类化。
优点是您可以直接在测试中调用方法,而不是尝试设置请求。控制器规范在auth方面可能很挑剔,因为它们没有会话和实际请求的环境。
然后我会向第一个控制器添加一个功能测试,该控制器实现身份验证以测试集成。