如何在单元测试中使用其他方法临时替换方法?

时间:2015-06-20 16:03:34

标签: ruby unit-testing minitest

使用Mocha来模拟或删除通过网络的函数非常有用:

def test_something
  NetworkClass.stub(:fetch_something, "contrived example")

  assert_equal "contrived example", NetworkClass.fetch_something
end

但更常见的情况是,我的网络交叉功能需要一些输入,而输入会影响函数返回的内容。对于我的单元测试,我只是准备一个Hash,其中包含我的测试套件需要运行的输入和输出。问题是,现在我不能再使用Mochas .stub.expects了,因为他们不允许我替换实际做某事的函数。

目前我正在做这样的事情:

def setup
  @subject = NetworkClass.new

  test_data = fetch_test_data

  @subject.define_singleton_method(:fetch_something) { |input|
    test_data[input] or fail "Unexpected stub input: #{input}"
  }
end

但是这会永久地改变主题,所以我不能使用这种方法来存根模块函数或类方法。

有没有办法暂时"用另一种方法替换方法,这种方法就像Mocha的.stub一样简单?

如果有人想告诉我这是一种不好的方式来测试并通知我一个优秀的方法,我想我也接受了这个答案。这一直困扰着我。

避免这种情况的一个显而易见的方法是在每个测试函数中记住方法时考虑到特定的输入,但在我看来,这似乎是很多额外的"样板"测试代码没有实际的好处。另外,通过这种方式,我可以肯定的是,我不会忘记在某个地方存储该方法,并且每次运行我的测试套件时都会意外地通过网络。

2 个答案:

答案 0 :(得分:1)

就像你说的,如果你知道测试中的输入是什么,你可能仍然可以使用mocha的功能。

NetworkClass.stub(:fetch_something).with(input).returns(test_data[input])

仅当使用正确的输入调用fetch_something时才会执行此操作。您可以使用不同的输入以这种方式创建多个存根。

一般而言,这应该不是问题,因为您的测试应该是可预测和可重复的。这意味着每个单独的测试用例应该运行相同的代码路径,从而为您的NetworkClass触发相同的参数。此外,每个测试用例应运行单个代码路径并尽可能集中。

修改

不确定您的测试框架是什么,但在每个测试用例之前应该有设置和拆卸的机制。这样你就可以使用你的方法并在每个测试用例之后恢复类。

答案 1 :(得分:-1)

在RSpec 3中,您可以使用allow

allow(NetworkClass).to receive(:fetch_something).with(input) { test_data[input] }

或(对于更多颗粒状测试)

allow(NetworkClass).to receive(:fetch_something) do |arg|
  expect(arg).to eql test_data[input]
end