Rails中的RSpec 3最佳实践和expect_any_instance_of

时间:2017-08-31 23:10:28

标签: ruby-on-rails rspec

RSpec文档显然是opposed to expect_any_instance_of,表示它只应用于遗留代码,因此我正在寻找最佳实践替代方案。

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

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

1 个答案:

答案 0 :(得分:1)

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

有些事情可能会对你有所帮助:

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) }

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

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

  1. 检查返回的内容,而不是它的工作原理:
  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
    

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

    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
    
    1. 最后有时你真的需要存根任何实例。而且没关系 - 有时会发生一些事情:)
    2. 有时你也可以用这样的存根替换any_instance:

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

      我希望它会对你有所帮助,我希望它不会太久:)