在期望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:
- 首先评估
@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
,你会告诉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
或$stderr
) StringIO, 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.
– 从这个答案