为什么在测试时模拟数据?
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
我正在尝试学习一些测试,并且偶然发现了模拟和存根。我实际上认为我可以在这里有所不同,我想不出我应该首先使用模拟的任何理由。 让我们看看 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