据我所知,在单元测试中methods should be isolated from their dependencies,这样他们就不会受到环境变化的影响。
然而,删除所有依赖项让我觉得我正在测试实现而不是行为。
换句话说,通过隔离依赖关系,我将测试与实现细节相结合。因此,任何code refactoring都会导致测试失败,即使行为(期望的结果)没有改变。
例如,这是一个简单的(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
测试正在通过,但我看到一些严重的问题:
我一定是做错了。
编写单元测试的正确方法是什么?
答案 0 :(得分:4)
我不认为你在这里完全不合适,正如AlistairIsrael所说。
您可以采取一些优化措施,使其更简洁。一个好的测试应该清楚地显示您对代码的期望。
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 => page_params = mock('page_params')
)
# not needed as .should_receive creates a nil stub by default.
# 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
subject.should_not_receive(:update_attributes)
page.send_request
end
end
end
从上面的一些变化中你可以看到rspec的双辅助非常强大,你可以构造复杂的对象并使用一些赋值,你可以访问链中最后一个被评估的方法。
我对否定案例做了一个假设,但你应该明白这个想法。测试update_attributes
的方法调用可能更容易,更清晰,因为你知道page_params
永远不会被激活?条件不符合。
HTH