据我了解,在单元测试中,方法应该与其依赖项隔离,这样它们就不会受到环境变化的影响。
尽管如此,删除所有依赖项让我觉得我正在测试实现而不是行为。
换句话说,通过隔离依赖关系,我将我的测试与实现细节耦合起来。因此,任何代码重构都会导致测试失败,即使行为(期望的结果)没有改变。
例如,这是一个简单的 (Ruby) 方法:
def send_request
update_attributes(response.page_params) if active?
end
这些是我对这行代码的两个独立测试:
let(:page) { Page.new }
describe '#send_request' do
context 'when a page is active' do
it 'updates page with the response parameters' do
page.active = true
response = double('response')
page_params = double('page_params')
response.stub(:page_params).and_return(page_params)
page.stub(:response).and_return(response)
page.stub(:update_attributes).and_return(nil)
page.should_receive(:update_attributes).with(page_params)
page.send_request
end
end
context 'when a page is inactive' do
it 'does NOT send a request' do
page.active = false
page.should_not_receive(:response)
page.send_request
end
end
end
测试通过了,但我发现了一些严重的问题:
- 如果稍后我决定使用 update_attributes() 以外的任何其他方法将更改保存到数据库中,我的测试将失败,即使数据将按预期保存
- 如果 response.page_params 的实现发生变化,我的软件将在生产中失败,但测试仍将通过
我一定做错了什么。
编写单元测试的正确方法是什么?