【问题标题】:Rspec mocks, can 'expect' also stub a method as a side effect?Rspec模拟,“期望”也可以将方法存根作为副作用吗?
【发布时间】:2014-05-05 11:42:55
【问题描述】:

我正在尝试理解继承应用程序中的测试,我需要一些帮助。

有很多这样的规范组(查看规范):

let(:job_post) { FactoryGirl.create(:job_post) }

# ...

before do
  expect(view).to receive(:job_post).at_least(:once).and_return(job_post)
end

it "should render without error" do
  render
end

... job_post 是控制器上定义的辅助方法。 (是的,他们本可以使用@instance 变量,而我正在重构它)。

现在,我认为在 before 块内使用 expect 是错误的。让我们暂时忘记这一点。

上面的测试通常是绿色的。
但是,如果我删除 expect 行,测试将失败。在这种情况下,expect 似乎在视图上存根方法。其实把expect换成allow好像效果完全一样。

我认为发生的情况是正常情况下 - 当与服务器一起运行时 - 视图将调用 job_posts 并且消息将登陆控制器上的辅助方法,这是预期的行为。

然而,expect 设置了一个期望值,同时在view 上使用一个固定的返回值存根方法。由于视图模板调用该方法,因此测试通过。

关于expect 的意外“存根”副作用,我在rspec-mocks readme 中找到了这个:

(...) 我们也可以设置一个消息期望,这样如果 find 没有被调用,例子就会失败:

person = double("person")
expect(Person).to receive(:find) { person }

RSpec 用它自己的类似 test-double 的方法替换我们存根或模拟的方法。在示例结束时,RSpec 会验证任何消息预期,然后恢复原始方法。

有人对该方法的这种特定使用有任何经验吗?

【问题讨论】:

    标签: ruby-on-rails ruby rspec rspec-mocks


    【解决方案1】:

    嗯,这就是expect().to receive() 所做的!这是rspec 的(不是)新的expectation syntax,它取代了should_receive API

    expect(view).to receive(:job_post).at_least(:once).and_return(job_post)
    

    等价于

    view.should_receive(:job_post).at_least(:once).and_return(job_post)
    

    这个API设置期望返回值。这是默认行为。要真正调用原始方法,您需要明确说明:

    view.should_receive(:job_post).at_least(:once).and_call_original
    

    关于其他一些问题:

    (是的,他们本可以使用@instance 变量,而我正在重构它)。

    let API 在 rspec 测试中非常普遍,并且在很多情况下可能比 @instance 变量更好(例如 - 它是惰性的,因此仅在需要时运行,并且它是记忆化的,因此最多运行一次)。

    其实把expect换成allow好像效果完全一样。

    allow 语法取代了旧 rspec 语法中的stub 方法,所以是的,它具有相同的效果,但不同的是,如果存根方法是 没有调用。


    按照 OP 的要求 - 关于 should_receive 的一些解释 - 单元测试预计将单独运行。这意味着不应该直接测试的所有内容都不应被测试。这意味着 HTTP 调用、IO 读取、外部服务、其他模块等不属于测试的一部分,并且出于 测试的目的,您应该假设它们工作正常。

    应该在您的测试中包含的是那些 HTTP 调用、IO 读取和外部服务被正确调用。为此,您需要设置消息期望 - 您期望被测试的方法调用某个方法(其实际功能超出了考试)。所以你期望服务接收一个方法调用,使用正确的参数,一次或多次(你可以明确地期望它应该被调用多少次),并且,作为实际调用的交换,您将其存根,并根据测试设置其返回值。

    来源:

    【讨论】:

    • 谢谢。当我提到实例变量时,我实际上是在谈论控制器,而不是测试。在我之前从事该项目的人不喜欢使用控制器的 @instance 变量的视图,并为所有这些变量创建了访问器。
    • 另外,很好的答案...但是您能否扩展should_receive?我一直使用stuballow,我一直认为expect(obj).to receive 设置的期望
    • 感谢您的澄清。是的,我知道模拟和存根在单元测试中的重要性。但是,我总是明确地使用stub/allow 来存根方法,然后使用shout_receive/expect().to receive 来测试消息。我很惊讶expect 会自动处理它……老实说,我觉得这有点令人困惑。
    【解决方案2】:

    Rspec 是一个 meta-gem,它依赖于 rspec-core、rspec-expectations 和 rspec-mocks gems。 Rspec-mocks 是 rspec 的测试替身框架,支持方法存根、伪造品和对生成的测试替身和真实对象等的消息期望。

    allow().to receive
    

    不过是使用“方法存根”

    expect().to receive
    

    是使用“消息期望”

    您可以参考Doc了解更多详情

    【讨论】:

      【解决方案3】:

      如果您不想将存根作为副作用,您可以随时调用原始代码。

      https://relishapp.com/rspec/rspec-mocks/v/2-14/docs/message-expectations/calling-the-original-method

      例如,我曾经想监视一个方法,但也调用该函数,否则它会产生其他副作用。这真的很有帮助。

      https://relishapp.com/rspec/rspec-mocks/v/2-14/docs/message-expectations/calling-the-original-method

      【讨论】:

        猜你喜欢
        • 2021-12-31
        • 1970-01-01
        • 2011-10-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多