【发布时间】: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中使用括号?
【问题讨论】:
我有一个测试是这样做的:
expect(@parser.parse('adsadasdas')).to raise_error(Errno::ENOENT)
它没有工作。我改为:
expect { @parser.parse('adsadasdas') }.to raise_error(Errno::ENOENT)
它奏效了。
我们什么时候使用花括号,什么时候我们在expect中使用括号?
【问题讨论】:
在用括号编写的测试中,代码正常执行,包括所有正常的错误处理。花括号语法定义了一个块对象,您可以在其上放置期望。它封装了您希望被破坏的代码,并允许 rspec 捕获错误并提供自己的处理(在这种情况下,测试成功)。
您也可以这样想:使用括号,代码在传递给expect 方法之前执行,但使用块,expect 将运行代码本身。
【讨论】:
expect { @parser.parse(url) }.to include(known_url) 不起作用,但如果我将它与括号一起使用,它会起作用。为什么?
为了回应 OP 的评论,我已经编辑并完全重写了我的答案。我意识到我最初的答案过于简单化了,以至于它可能被认为是不正确的。
这个StackOverflow question实际上已经解决了你的问题。
一位发帖者Peter Alfvin 说得很好:
至于规则,如果您尝试测试,则通过块或 Proc 行为(例如引发错误、更改某些值)。否则,你 传递一个“常规”参数,在这种情况下,该参数的值 参数是经过测试的。
您遇到这种现象的原因与错误的增加有关。当您将 @parser.parse('adsadasdas') 作为参数(使用括号)传递给 expect 时,您实际上是在告诉 ruby:
@parser.parse('adsadasdas')。expect。expect 应该看看这个结果是否符合我的预期(也就是说,Errno:ENOENT 会被提升)。但是,发生的情况是:当 ruby 评估 @parser.parse('adsadasdas') 时,会立即引发错误。 Ruby 甚至没有机会将结果传递给expect。 (对于我们所关心的,您可以将@parser.parse('adsadasdas') 作为参数传递给任何函数......比如multiply() 或capitalize())引发错误,expect 甚至没有机会完成它的工作。
但是,当您使用花括号将 @parser.parse('adsadasdas') 作为 proc(代码块)传递给 expect 时,您告诉 ruby 的是:
expect,准备工作吧。expect,我希望您跟踪我们评估 @parser.parse('adsadasdas') 时发生的情况。expect,刚刚评估的代码块是否引发了Errno:ENOENT 错误?我期待它会。当您将代码块传递给expect 时,您是在告诉expect 您希望它检查结果行为、代码块执行所做的更改,然后让您知道它是否满足达到您提供的期望。
当您将参数传递给 expect 时,您是在告诉 ruby 评估该参数以达到某个值 before expect 甚至参与其中,然后您正在将该值传递给expect,以查看它是否符合预期。
【讨论】:
简而言之:
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
【讨论】:
TL;DR:使用expect(exp) 指定有关exp 的值 的内容,并使用expect { exp } 指定@987654326 时发生的副作用 @ 被执行。
让我们稍微解开一下。大多数 RSpec 的匹配器都是 value 匹配器。它们匹配(或不匹配)任何红宝石对象。相比之下,少数 RSpec 的匹配器只能与块匹配,因为它们必须在块运行时观察块才能正常运行。这些匹配器关注块执行时发生(或不发生)的副作用。匹配器无法判断指定的副作用是否已经发生,除非它被传递一个要执行的块。让我们一一考虑内置的块匹配器(从 RSpec 3.1 开始):
raise_error考虑可以从方法返回异常,这与引发异常不同。引发异常是一种副作用,匹配器只能通过使用适当的rescue 子句执行块来观察。因此,这个匹配器必须接收一个块才能正常工作。
throw_symbol投掷符号类似于引发错误——它会导致堆栈跳转并且是一种副作用,只能通过在适当的catch 块内运行块来观察。
change状态突变是一种副作用。匹配器只能通过检查之前的状态、运行块,然后检查之后的状态来判断某个状态是否发生了变化。
outputI/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 无法检查匹配器以查看是否支持值或块期望。