使用Rspec进行测试和模拟服务

7
我创建了一个名为Transaction的服务对象,它处理订单、设置支付并建立模型关联。
这个类叫做Transaction,有两个方法initialize和pay。我在spec/services/中测试它(它位于app/services/中)。
initialize方法接收一个账户和一些用户传入的参数以处理订单。
我正试图用rspec测试pay方法,但这个函数包含了很多内容。例如,它会创建新的模型,然后建立它们之间的一些关联。
到目前为止,我已经创建了一个double账户,如下所示:
@account = double("account", :confirmed => true, :confirmed? => true)
然而,在Transaction pay方法中使用了许多其他函数和关联。因此,当我在测试中调用Transaction.pay时,一旦调用这些关联,就会返回一个错误:
ActiveRecord::AssociationTypeMismatch:
Business(#70250016824700) expected, got RSpec::Mocks:.

商业模式与账户一样是双重的,作为属性添加到@account中。当Transaction.pay在其中创建新的模型和关联时,我该如何测试它?

我需要像上面展示的一样mock它们吗?还是有更好的方法可以做到呢?

我正在使用FactoryGirl,但由于我的account模型使用了Devise,我无法使用它。因为Devise测试助手不能在控制器之外使用,而且由于我正在服务中进行测试,所以它无法工作。


FactoryGirl和Devise不应该是问题。服务测试应该类似于模型测试。你应该能够在你的模型中使用FactoryGirl,对吧? - phoet
1个回答

8
很难具体说明,因为你没有提供Transaction类代码,但是在高层次上,这是我建议的:
  • 不要使用FactoryGirl或Devise助手 - 理想情况下,您正在为此类编写单元测试,因此不希望有任何外部依赖项。
  • 存根化您正在处理的任何模型实例 - 这就是您对account所做的操作。
  • 存根化服务中使用的任何外部类(例如Business),如果您从类创建这些实例,则它们也是doubles。
这是一个没有实际实现的模糊示例:
require './app/services/transaction'

describe Transaction do
  let(:transaction) { Transaction.new account, :foo => 'bar' }
  let(:account)     { double 'Account', :confirmed? => true }
  let(:business)    { double 'Business', :save => true }

  before :each do
    stub_const 'Business', double(:new => business)
  end

  describe '#pay' do
    it "does stuff" do
      # ...

      transaction.pay

      # ...
    end
  end
end

请注意,我只需要交易文件——这有助于你了解它依赖的其他类,因为你(理想情况下)将存根常量,或者如果必要,需要其他文件。这也可以保持此规范的运行时间更好。
另外,我只是在账户double上存根了confirmed?而不是confirmed。从Rails的角度来看,它们的意思是一样的,但最好始终使用其中一个而不是两个。
如果你有大量的设置(很多常量和测试double),那么我会认为这个Transaction类可能需要被拆分成几个更具体的类。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接