1

有时我会遇到想在测试中使用类方法的部分模拟的情况。目前,我正在使用不支持此功能的minitest (可能是因为这首先不是一个好主意......)。

一个例子:

class ImportRunner

  def self.run *ids
    ids.each { |id| ItemImporter.new(id).import }
  end
end

class ItemImporter

  def initialize id
    @id = id
  end

  def import
    do_this
    do_that
  end

  private

    def do_this
      # do something with fetched_data
    end

    def do_that
      # do something with fetched_data
    end

    def fetched_data
      @fetched_data ||= DataFetcher.get @id
    end

end

我想单独测试该ImportRunner.run方法(主要是因为ItemImporter#import它很慢/很贵)。在rspec我会写一个这样的测试:

it 'should do an import for each id' do
  first_importer  = mock
  second_importer = mock

  ItemImporter.should_receive(:new).with(123).and_return(first_importer)
  first_importer.should_receive(:import).once
  ItemImporter.should_receive(:new).with(456).and_return(second_importer)
  second_importer.should_receive(:import).once

  ImportRunner.run 123, 456
end

问题的第一部分:是否可以在minitest中做类似的事情?


问题的第二部分:对象协作是不是形式

collaborator = SomeCollaborator.new a_param
collaborator.do_work

糟糕的设计?如果是这样,你会如何改变它?

4

2 回答 2

1

您所要求的几乎可以在直接 Minitest 中实现。Minitest::Mock 不支持部分模拟,因此我们尝试通过存根 ItemImporter 的new方法并返回一个 lambda,该 lambda 调用一个返回模拟的模拟。(模拟中的模拟:Mockception)

def test_imports_for_each_id
  # Set up mock objects
  item_importer   = MiniTest::Mock.new
  first_importer  = MiniTest::Mock.new
  second_importer = MiniTest::Mock.new

  # Set up expectations of calls
  item_importer.expect :new, first_importer,  [123]
  item_importer.expect :new, second_importer, [456]
  first_importer.expect  :import, nil
  second_importer.expect :import, nil

  # Run the import
  ItemImporter.stub :new, lambda { |id| item_importer.new id } do
    ImportRunner.run 123, 456
  end

  # Verify expectations were met
  # item_importer.verify
  first_importer.verify
  second_importer.verify
end

除了调用item_importer.verify. 因为该模拟将返回其他模拟,所以验证所有期望是否满足的过程将在first_importer和模拟上调用其他方法second_importer,从而引发它们。因此,虽然您可以接近,但您无法准确复制您的 rspec 代码。为此,您必须使用支持部分模拟的不同模拟库,如RR

如果该代码在您看来很难看,请不要担心,确实如此。但这不是 Minitest 的错,而是测试中职责冲突的错。就像你说的,这可能不是一个好主意。我不知道这个测试应该证明什么。它看起来是在指定代码的实现,但它并没有真正传达预期的行为。这就是一些人所说的“过度嘲笑”。

模拟和存根是开发人员手中重要且有用的工具,但很容易被带走。除了给人一种虚假的安全感之外,过度模拟的测试也可能是脆弱和嘈杂的。- Rails 反模式

我会重新考虑你想通过这个测试来完成什么。Minitest 通过做出丑陋的东西应该看起来丑陋的设计选择来帮助您。

于 2013-09-10T21:49:12.890 回答
0

您可以使用Mocha gem。我也在我的大部分测试中使用 MiniTest,并使用 Mocha 来模拟和存根方法。

于 2013-08-17T21:16:00.807 回答