【问题标题】:When to use curly braces vs parenthesis in expect Rspec method?何时在期望 Rspec 方法中使用花括号与括号?
【发布时间】:2014-03-01 07:53:02
【问题描述】:

我有一个测试是这样做的:

expect(@parser.parse('adsadasdas')).to raise_error(Errno::ENOENT)

它没有工作。我改为:

expect { @parser.parse('adsadasdas') }.to raise_error(Errno::ENOENT)

它奏效了。

我们什么时候使用花括号,什么时候我们在expect中使用括号?

【问题讨论】:

    标签: ruby rspec


    【解决方案1】:

    在用括号编写的测试中,代码正常执行,包括所有正常的错误处理。花括号语法定义了一个块对象,您可以在其上放置期望。它封装了您希望被破坏的代码,并允许 rspec 捕获错误并提供自己的处理(在这种情况下,测试成功)。

    您也可以这样想:使用括号,代码在传递给expect 方法之前执行,但使用块,expect 将运行代码本身。

    【讨论】:

    • 感谢您的回答。我仍然不明白什么时候可以将期望与 {} 或 () 一起使用。 expect { @parser.parse(url) }.to include(known_url) 不起作用,但如果我将它与括号一起使用,它会起作用。为什么?
    【解决方案2】:

    为了回应 OP 的评论,我已经编辑并完全重写了我的答案。我意识到我最初的答案过于简单化了,以至于它可能被认为是不正确的。

    这个StackOverflow question实际上已经解决了你的问题。

    一位发帖者Peter Alfvin 说得很好:

    至于规则,如果您尝试测试,则通过块或 Proc 行为(例如引发错误、更改某些值)。否则,你 传递一个“常规”参数,在这种情况下,该参数的值 参数是经过测试的。

    您遇到这种现象的原因与错误的增加有关。当您将 @parser.parse('adsadasdas') 作为参数(使用括号)传递给 expect 时,您实际上是在告诉 ruby​​:

    1. 首先评估@parser.parse('adsadasdas')
    2. 获取结果并将其传递给expect
    3. expect 应该看看这个结果是否符合我的预期(也就是说,Errno:ENOENT 会被提升)。

    但是,发生的情况是:当 ruby​​ 评估 @parser.parse('adsadasdas') 时,会立即引发错误。 Ruby 甚至没有机会将结果传递给expect。 (对于我们所关心的,您可以将@parser.parse('adsadasdas') 作为参数传递给任何函数......比如multiply()capitalize())引发错误,expect 甚至没有机会完成它的工作。

    但是,当您使用花括号将 @parser.parse('adsadasdas') 作为 proc(代码块)传递给 expect 时,您告诉 ruby​​ 的是:

    1. expect,准备工作吧。
    2. expect,我希望您跟踪我们评估 @parser.parse('adsadasdas') 时发生的情况。
    3. 好的,expect,刚刚评估的代码块是否引发了Errno:ENOENT 错误?我期待它会。

    当您将代码块传递给expect 时,您是在告诉expect 您希望它检查结果行为、代码块执行所做的更改,然后让您知道它是否满足达到您提供的期望。

    当您将参数传递给 expect 时,您是在告诉 ruby 评估该参数以达到某个值 before expect 甚至参与其中,然后您正在将该值传递给expect,以查看它是否符合预期。

    【讨论】:

    【解决方案3】:

    简而言之:

    • 使用花括号(一个块):当你想测试behavior
    • 当你想测试returned value时使用括号

    值得一读:As for rules, you pass a block or a Proc if you're trying to test behavior (e.g. raising errors, changing some value). Otherwise, you pass a "conventional" argument, in which case the value of that argument is what is tested. - from this answer

    【讨论】:

    • 能够以简明扼要的方式总结事情是一种天赋。
    【解决方案4】:

    TL;DR:使用expect(exp) 指定有关exp 的内容,并使用expect { exp } 指定@987654326 时发生的副作用 @ 被执行。


    让我们稍微解开一下。大多数 RSpec 的匹配器都是 value 匹配器。它们匹配(或不匹配)任何红宝石对象。相比之下,少数 RSpec 的匹配器只能与块匹配,因为它们必须在块运行时观察块才能正常运行。这些匹配器关注块执行时发生(或不发生)的副作用。匹配器无法判断指定的副作用是否已经发生,除非它被传递一个要执行的块。让我们一一考虑内置的块匹配器(从 RSpec 3.1 开始):

    raise_error

    考虑可以从方法返回异常,这与引发异常不同。引发异常是一种副作用,匹配器只能通过使用适当的rescue 子句执行块来观察。因此,这个匹配器必须接收一个块才能正常工作。

    throw_symbol

    投掷符号类似于引发错误——它会导致堆栈跳转并且是一种副作用,只能通过在适当的catch 块内运行块来观察。

    change

    状态突变是一种副作用。匹配器只能通过检查之前的状态、运行块,然后检查之后的状态来判断某个状态是否发生了变化。

    output

    I/O 是一个副作用。要使output 匹配器工作,它必须用新的StringIO, execute the block, restore the stream to its original value, and then check the contents of theStringIO` 替换适当的流($stdout$stderr)。

    yield_control/yield_with_args/yield_with_no_args/yield_with_successive_args

    这些匹配器有点不同。 Yielding 并不是真正的副作用(它实际上只是调用调用者提供的另一个函数的语法糖),但是通过查看表达式的返回值无法观察到 Yield。为了让产量匹配器工作,它们提供了一个 probe 对象,您可以使用 &probe 语法将其作为块传递给被测方法:

    expect { |probe| [1, 2, 3].each(&probe) }.to yield_with_successive_args(1, 2, 3)
    

    所有这些匹配器有什么共同点?它们都不能处理简单的红宝石值。相反,它们都必须在适当的上下文中包装一个块(即拯救、捕获或检查之前/之后的值)。

    请注意,在 RSpec 3 中,我们 added some logic 在用户使用错误的 expect 表单和给定的匹配器时为他们提供明确的错误。但是,在 expect(do_something).to raise_error 的特定情况下,我们无法为您提供明确的解释——如果 do_something 引发错误(如您所料......),则在 ruby​​ 之前引发错误评估 to 参数(raise_error 匹配器),因此 RSpec 无法检查匹配器以查看是否支持值或块期望。

    猜你喜欢
    • 1970-01-01
    • 2014-04-23
    • 2011-01-12
    • 1970-01-01
    • 1970-01-01
    • 2019-01-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多