我的应用程序正在为所有应用程序设置current_user,所以在创建测试之前,这是ok的,在主控制器中,我正在设置用户
class ApplicationController < ActionController::API
...
def authenticate_action
end
现在我正在写测试:
RSpec.describe 'Api::V1::Articles', type: :request do
let(:user) { FactoryBot.build_stubbed :user }
describe 'POST /create' do
context "with valid user params" do
let!(:articles_params) { {article:{ name: "art1" } }}
it 'creates a new article' do
expect { post "/api/v1/posts/1/articles", params: articles_params } .to change(Article, :count).by(1)
end
end
end
end
但是测试中的用户只是假的,这就是为什么我得到这个错误:
Completed 400 Unauthorized
这取决于您是否也想对每个请求进行验证。如果是这种情况,您需要为该用户设置一个有效的认证头,如下所示:
RSpec.describe 'Api::V1::Articles', type: :request do
let(:user) { FactoryBot.create :user }
let(:auth_header) { { 'Authorization' => "TOKEN#{user.generate_bearer_token}" } }
describe 'POST /create' do
context "with valid user params" do
let(:articles_params) { {article:{ name: "art1" } }}
it 'creates a new article' do
expect {
post "/api/v1/posts/1/articles", params: articles_params, headers: auth_header
}.to change(Article, :count).by(1)
end
end
end
end
请注意,您需要使用应用程序中的实现更改上述示例的第三行中的"TOKEN#{user.generate_bearer_token}"
,以便为给定用户生成有效的承载令牌。
或您可以决定不关心身份验证是如何实现的,并在测试中模拟整个身份验证逻辑。然后,当然,您必须在其他地方测试身份验证逻辑,以确保实现实际正常工作。
为了模拟实现,我将首先将authenticate_action
方法的部分移动到用户模型中的类方法或Auth
命名空间中的类中,然后仅调用authenticate_action
中的一个方法:
# in app/models/user.rb
def self.find_by_bearer_token(token)
id = Auth::TokenValidator.call(token).result
User.find(id) if id
end
# in your application_controller
def authenticate_action
@user = User.find_by_bearer_token(bearer_token)
render json: 400, status: :unauthorized unless @user
end
使用这样的类方法,在测试中模拟该方法并返回您想要用于规范的任何用户要容易得多:
RSpec.describe 'Api::V1::Articles', type: :request do
let(:user) { FactoryBot.create :user }
before { allow(User).to receive(:find_by_bearer_token).and_return(user) }
describe 'POST /create' do
context "with valid user params" do
let(:articles_params) { {article:{ name: "art1" } }}
it 'creates a new article' do
expect {
post "/api/v1/posts/1/articles", params: articles_params
}.to change(Article, :count).by(1)
end
end
end
end