ruby-on-rails – Rails中的RSpec 3最佳实践和expect_any_instance_of

RSpec文档显然是
opposed to expect_any_instance_of,说它应该只用于遗留代码,所以我正在寻找最佳实践替代方案.

在我想测试在满足某些条件时调用方法但是在不同范围内加载对象的情况下,我会使用expect_any_instance_of.

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

最佳答案 好吧好吧.答案是 – 这取决于:)

有些事情可能对您有所帮助:

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

假设你有这个类:

class UserUpdater
  def update(user)
    user.update_attributes(updated: true)
  end
end

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

把一切都记住:

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

最小(或没有)存根:

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

it { expect { update }.to change { user.reload.updated }.to(true) }

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

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

>检查返回的内容,而不是它的工作原理:

例如,您有一个服务,可以为您找到或构建用户:

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

你可以测试它而不需要在任何实例上进行存根:

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

context 'when user not exists' 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

>最后有时你真的需要存根任何实例.它没关系 – 有时狗屎发生:)

有时你也可以用这样的stub替换any_instance:

allow(File).to receive(:open).and_return(my_file_double)

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

点赞