RSpec 3最佳实践和Rails中的Expect_any_instance_of

Dan*_*iel 5 rspec ruby-on-rails

RSpec文档显然与Expect_any_instance_of相对,称它只应在旧代码上使用,因此我正在寻找最佳实践的替代方案。

在需要测试满足特定条件时将调用某个方法但将对象加载到其他范围中的情况下,我经常使用Expect_any_instance_of。

例如,在编写控制器规范时,我只想测试在X实例上使用正确的参数调用了正确的方法。

小智 7

好吧好吧。答案是 - 这取决于:)

以下是一些可能对您有所帮助的事情:

1)看看你测试代码的方式。(通常)有两种方法可以做到。

假设你有这个类:

class UserUpdater
  def update(user)
    user.update_attributes(updated: true)
  end
end
Run Code Online (Sandbox Code Playgroud)

然后你可以通过两种方式测试它:

存根一切:

it 'test it' do
  user = double(:user, update_attributes: true)
  expect(user).to receive(:update_attributes).with(updated: true)
  UserUpdater.new.update(user)
end
Run Code Online (Sandbox Code Playgroud)

最小(或没有)存根:

let(:user) { FactoryGirl.create(:user) }
let(:update) { UserUpdater.new.update(user) }

it { expect { update }.to change { user.reload.updated }.to(true) }
Run Code Online (Sandbox Code Playgroud)

我更喜欢第二种方式——因为它更自然,让我对我的测试更有信心。

回到您的示例 - 您确定要在控制器操作运行时检查方法调用吗?在我看来 - 最好检查结果。它背后的一切都应该单独测试——例如,如果你的控制器有一个服务被调用——你将在它自己的规范中测试关于这个服务的所有内容,以及控制器规范中操作的一般工作方式(某种集成测试)。

2. 检查返回的是什么,而不是它是如何工作的:

例如,您有一项服务,它可以为您查找或构建用户:

class CoolUserFinder
   def initialize(email)
      @email = email
   end

   def find_or_initialize
      find || initialize
   end

   private

   def find
     User.find_by(email: email, role: 'cool_guy')
   end

   def initialize
     user = User.new(email: email)
     user.maybe_cool_guy!

     user
   end
end
Run Code Online (Sandbox Code Playgroud)

您可以在不存根于任何实例的情况下对其进行测试:

let(:service) { described_class.new(email) }
let(:email) { 'foo@bar.org' }
let(:user) { service.find_or_initialize }

context 'when user does not exist' do
  it { expect(user).to be_a User }
  it { expect(user).to be_new_record }
  it { expect(user.email).to eq 'foo@bar.org' }
  it { expect(user.role).to eq 'maybe_cool_guy' }
  it { expect(user).to be_on_hold }
end

context 'when user already exists' do
  let!(:old_user) { create :user, email: email }

  it { expect(user).to be_a User }
  it { expect(user).not_to be_new_record }
  it { expect(user).to eq old_user }
  it { expect(user.role).to eq 'cool_guy' }
  it { expect(user).not_to be_on_hold }
end
Run Code Online (Sandbox Code Playgroud)

3. 最后,有时您真的需要存根任何实例。没关系 - 有时会发生狗屎:)

有时你也可以像这样用存根替换 any_instance :

allow(File).to receive(:open).and_return(my_file_double)
Run Code Online (Sandbox Code Playgroud)

我希望它会帮助你一点,我希望它不会太长:)