在期望Rspec方法中何时使用花括号与括号?

我做了一个测试:

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

它不起作用。 我改为:

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

它奏效了。

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

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

您的问题实际上是由其他StackOverflow问题解决的 。

一张海报, 彼得阿尔文 ,他说:

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

你遇到这种现象的原因与引发错误有关。 当您将@parser.parse('adsadasdas')作为参数传递(使用括号)时,您实际上是在告诉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 ,你会告诉ruby expect参与之前评估该参数是否达到某个值,然后你传递该值以expect它是否符合某种期望。

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


让我们解开一下。 RSpec的大多数匹配器都是价值匹配器。 它们匹配(或不匹配)任何ruby对象。 相比之下,少数RSpec的匹配器只能与块匹配,因为它们必须在运行时观察块才能正常运行。 这些匹配器涉及块执行时发生(或不发生)的副作用。 除非传递一个块来执行,否则匹配器无法判断是否已发生命名的副作用。 让我们一个一个地考虑内置块匹配器(从RSpec 3.1开始):

raise_error

考虑一个人可以从方法返回exception,这与提出exception不同。 提出exception是一种副作用,只有匹配器通过执行具有适当rescue条款的块才能观察到exception。 因此,此匹配器必须接收块才能正常工作。

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 the替换适当的流( $stdout$stderrStringIO, execute the block, restore the stream to its original value, and then check the contents of the StringIO` StringIO, execute the block, restore the stream to its original value, and then check the contents of the

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) 

所有这些匹配者有什么共同之处? 他们都不能处理简单的ruby值。 相反,它们都必须在适当的上下文中包含一个块(即在值之前/之后进行抢救,捕获或检查)。

请注意,在RSpec 3中,我们添加了一些逻辑 ,以便在用户使用给定匹配器的错误expect表单时为用户提供明确的错误。 但是,在expect(do_something).to raise_error的特定情况下,我们无法为您提供明确的解释 – 如果do_something引发错误(如您所期望的那样……),则会引发错误在ruby评估to参数( raise_error匹配器)之前,RSpec无法检查匹配器以查看是否支持值或阻止期望。

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

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

简而言之:

  • 使用花括号(一个块):当你想测试behavior
  • 如果要测试returned value ,请使用括号

值得一读: As for rules, you pass a block or a Proc if you're trying to test behavior (eg raising errors, changing some value). Otherwise, you pass a "conventional" argument, in which case the value of that argument is what is tested. As for rules, you pass a block or a Proc if you're trying to test behavior (eg raising errors, changing some value). Otherwise, you pass a "conventional" argument, in which case the value of that argument is what is tested. – 从这个答案