RSpec:请求规范中的存根控制器方法

RSpec: Stub controller method in request spec

我正在写一个 RSpec request spec,它看起来大致像(为简洁起见略微缩短):

describe 'Items', type: :request do
  describe 'GET /items' do
    before do
      allow_any_instance_of(ItemsController).to receive(:current_user).and_return(user)
      get '/items'
      @parsed_body = JSON.parse(response.body)
    end

    it 'includes all of the items' do
      expect(@parsed_body).to include(item_1)
      expect(@parsed_body).to include(item_2)
    end
  end
end

控制器看起来像:

class ItemsController < ApplicationController
  before_action :doorkeeper_authorize!
  def index
    render(json: current_user.items)
  end
end

如您所见,我正在尝试对门卫的 current_user 方法进行 stub。

目前测试通过,控制器按预期工作。我的问题是关于这条线的:

allow_any_instance_of(ItemsController).to receive(:current_user).and_return(user)

我根据 How to stub ApplicationController method in request spec, and it works. However, the RSpec docs call it a "code smell" 中的答案写了这一行,rubocop-rspec 抱怨说,“RSpec/AnyInstance: Avoid stubbing using allow_any_instance_of”。

一种替代方法是获取对控制器的引用并使用 instance_double(),但我不确定如何从请求规范中获取对控制器的引用。

我应该如何编写此测试以避免代码异味/遗留测试方法?

你有没有想过不要嘲笑 current_user

如果您编写测试助手在您的请求规范之前登录 usercurrent_user 将自动填充,就好像它是真实用户一样。代码如下所示:

before do
  sign_in user
  get '/items'
  @parsed_body = JSON.parse(response.body)
end

如果您使用 devise gem 进行身份验证,它有一个很好的书面维基页面关于 here

@dhh

也推荐 here 这种方法

你应该去度假了。

我认为正确的方法是尽可能避免在请求规范中存根,看门人需要一个令牌来授权所以我会做类似的事情:

describe 'Items', type: :request do
  describe 'GET /items' do
    let(:application) { FactoryBot.create :oauth_application }
    let(:user)        { FactoryBot.create :user }
    let(:token)       { FactoryBot.create :access_token, application: application, resource_owner_id: user.id }
    before do
      get '/items', access_token: token.token
      @parsed_body = JSON.parse(response.body)
    end

    it 'includes all of the items' do
      expect(@parsed_body).to include(item_1)
      expect(@parsed_body).to include(item_2)
    end
  end
end

以下是 some examples 这些工厂的样子。

最后,好SO点!