3

据我了解,在单元测试中,方法应该与其依赖项隔离,这样它们就不会受到环境变化的影响。

尽管如此,删除所有依赖项让我觉得我正在测试实现而不是行为。

换句话说,通过隔离依赖关系,我将我的测试与实现细节耦合起来。因此,任何代码重构都会导致测试失败,即使行为(期望的结果)没有改变。

例如,这是一个简单的 (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 的实现发生变化,我的软件将在生产中失败,但测试仍将通过

我一定做错了什么。

编写单元测试的正确方法是什么?

4

1 回答 1

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如果处于活动状态将永远不会被调用?不满足条件。

高温高压

于 2012-12-04T08:45:38.287 回答