为什么在测试时模拟数据?

why mock data while testing?

我正在尝试学习一些测试,并且偶然发现了模拟和存根。我实际上认为我可以在这里有所不同,我想不出我应该首先使用模拟的任何理由。 让我们看看 Mocha gem 文档中的示例代码:

require 'test/unit'
require 'mocha/test_unit'

class MiscExampleTest < Test::Unit::TestCase
   test "mocking_an_instance_method_on_a_real_object" do
      person = Person.new
      person.expects(:save).returns(true)
      assert person.save

   end

我不明白这行背后的原因:

person.expects(:save).returns(true)

不管我用不用,结果都是一样的:测试通过。我能感觉到它与告诉 object/class 如何表现有关,但是如果我们首先告诉对象 return 为真然后检查它是否 returns ,那将如何测试真的?它总是会的,我们告诉过它。这不是没有意义吗?

不,它不会总是 return 正确。 测试是为了确保在您更改代码或更改其环境后,您的代码的每一寸都将 运行 符合预期。 如果您最近安装了新数据库、更改了主机或修改了 database.yaml,那么此 "save" 测试将 return 错误。 然后,这应该会在您的脑海中升起旗帜,在您解决该数据库连接错误之前不要推送到 master 。

你发的这个例子好像确实没什么道理:

test "mocking_an_instance_method_on_a_real_object" do
  person = Person.new
  person.expects(:save).returns(true)
  assert person.save
end

事实上,测试根本不测试 Person,它只是测试模拟是否有效。

但是 mocking 或 stubbing 很有意义:它允许你绕过你不感兴趣的部分(超出范围),通过不从你的数据库加载数据来加速你的测试套件,或者不调用外部服务。

想象以下示例:您有一个调用外部支付提供商的方法,可能 return 出错:

def make_payment(user, amount)
  payment = ExternalPaymentGateway.transfer(company_account, user.account, amount)
  raise 'payment error' if payment.error?
end

要测试此方法,您不想进行实际付款,也根本不想调用外部服务(因为不必要的 API 请求会大大降低您的测试套件速度)。相反,您想完全存根此服务:

ExternalPaymentGateway.expects(:transfer).returns(true)
assert_nothing_raised make_payment(user, '0000')

ExternalPaymentGateway.expects(:transfer).returns(false)
assert_raised 'payment_error' do
  make_payment(user, '0000')
end