我正在开发一个 api 应用程序,在此示例中,它有 2 种基本用户类型:
admin
和 user
。
对于本示例,资源将为
Widget
。管理员对控制器中的小部件具有完全访问权限,而用户则没有访问权限。现在,我在编写请求规范时经常重复自己。
EG
require 'rails_helper'
RSpec.describe '/widgets', type: :request do
context 'with an admin user' do
let(:user) { create(:user, is_admin: true) }
let(:valid_attributes) { attributes_for(:widget) }
let(:invalid_attributes) { attributes_for(:widget, name: nil) }
let(:valid_headers) { Devise::JWT::TestHelpers.auth_headers({}, user) }
describe 'GET /widgets' do
it 'renders a successful response' do
create(:widget)
get widgets_url, headers: valid_headers, as: :json
expect(response).to be_successful
end
end
end
context 'with a standard user' do
let(:user) { create(:user) }
let(:valid_attributes) { attributes_for(:widget) }
let(:invalid_attributes) { attributes_for(:widget, name: nil) }
let(:valid_headers) { Devise::JWT::TestHelpers.auth_headers({}, user) }
describe 'GET /widgets' do
it 'renders an unauthorized response' do
create(:widget)
get widgets_url, headers: valid_headers, as: :json
expect(response).to have_http_status(403)
end
end
end
end
现在想象一下这一切都是在 API 的整个 CRUD 上完成的,我的请求规范有 100 行长,并且有大量的复制粘贴,它们很难阅读,并且有点难以管理。
大部分样板都是相同的,我只是想实现不同的用户上下文来验证我的策略是否正在生效。
在完美的世界中,我将能够定义
describe
和 context
块一次,然后根据用户上下文做出不同的期望。
有什么建议可以去哪里看吗?
您可以调出大部分
let
,因为无论如何它们都会被延迟评估。
例如:
require 'rails_helper'
RSpec.describe '/widgets', type: :request do
let(:valid_attributes) { attributes_for(:widget) }
let(:invalid_attributes) { attributes_for(:widget, name: nil) }
let(:valid_headers) { Devise::JWT::TestHelpers.auth_headers({}, user) }
context 'with an admin user' do
let(:user) { create(:user, is_admin: true) }
describe 'GET /widgets' do
it 'renders a successful response' do
create(:widget)
get widgets_url, headers: valid_headers, as: :json
expect(response).to be_successful
end
end
end
context 'with a standard user' do
let(:user) { create(:user) }
describe 'GET /widgets' do
it 'renders an unauthorized response' do
create(:widget)
get widgets_url, headers: valid_headers, as: :json
expect(response).to have_http_status(403)
end
end
end
end
我建议不要进一步“干燥”。特别是测试应该详细说明它们所确保的功能。
您想要使用共享示例,甚至可能将其推入一个单独的文件中,您将其包含在
require_relative
和include_context
中
它会是这样的(请注意,我还重写了
subject
来发出请求并返回 request
,这样您就可以使用 is_expected.
简写语法 - 如果您更喜欢使用较长的 expect(response).
,则不需要这样做)语法)
describe '/widgets', type: :request do
let(:user_params) {{}}
let(:user) { create(:user, user_params) }
let(:valid_attributes) { attributes_for(:widget) }
let(:invalid_attributes) { attributes_for(:widget, name: nil) }
let(:valid_headers) { Devise::JWT::TestHelpers.auth_headers({}, user) }
let(:params) { {} }
let(:headers) { valid_headers }
subject do
process method, url, params: params, headers: headers, as: :json
response
end
shared_examples "successful response" do
it { is_expected.to have_http_status :ok }
end
shared_examples "unauthorized response" do
it { is_expected.to have_http_status :forbidden }
end
describe "GET /widgets" do
let(:method) { :get }
let(:url) { "/widgets" }
before { create(:widget) }
context 'with an admin user' do
let(:user_params) { { is_admin: true } }
it_behaves_like "successful response"
end
context 'with a standard user' do
it_behaves_like "unauthorized response"
end
end
end