不同用户上下文的 DRY 请求规范

问题描述 投票:0回答:2

我正在开发一个 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
块一次,然后根据用户上下文做出不同的期望。

有什么建议可以去哪里看吗?

ruby-on-rails rspec factory-bot pundit
2个回答
1
投票

您可以调出大部分

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

我建议不要进一步“干燥”。特别是测试应该详细说明它们所确保的功能。


0
投票

您想要使用共享示例,甚至可能将其推入一个单独的文件中,您将其包含在

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
© www.soinside.com 2019 - 2024. All rights reserved.