我做了一次测试:
expect(@parser.parse('adsadasdas')).to raise_error(Errno::ENOENT)
它不起作用。我改为:
expect { @parser.parse('adsadasdas') }.to raise_error(Errno::ENOENT)
它有效。
我们什么时候使用花括号?什么时候使用带括号的括号?
答案 0 :(得分:30)
为了回应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 在 expect
之前评估该参数是否达到某个值涉及,然后您将该值传递给expect
以查看它是否符合某些期望。
答案 1 :(得分:6)
TL; DR:使用expect(exp)
指定exp
的值,并使用expect { exp }
指定副作用在exp
执行时发生。
让我们解开一下。 RSpec的大多数匹配器都是 value 匹配器。它们匹配(或不匹配)任何ruby对象。相比之下,少数RSpec的匹配器只能与块匹配,因为它们必须在运行时观察块才能正常运行。这些匹配器涉及块执行时发生(或不发生)的副作用。除非传递一个块来执行,否则匹配器无法判断是否已发生命名副作用。让我们一个一个地考虑内置块匹配器(从RSpec 3.1开始):
raise_error
考虑可以从方法返回异常,这与引发异常不同。引发异常是一种副作用,只有匹配器通过执行具有适当rescue
子句的块才能观察到异常。因此,此匹配器必须接收块才能正常工作。
throw_symbol
抛出符号类似于引发错误 - 它会导致堆栈跳转,这是一种副作用,只能通过在适当的catch
块内运行块来观察。
change
对状态的突变是副作用。匹配器只能通过事先检查状态,运行块,然后检查状态来判断某个状态是否有变化。
output
I / O是副作用。要使output
匹配器正常工作,必须使用新的$stdout
StringIO`替换相应的流($stderr
或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中,我们added some logic为用户在使用给定匹配器的错误expect
表单时提供明确错误。但是,在expect(do_something).to raise_error
的特定情况下,我们无法为您提供明确的解释 - 如果do_something
引发错误(如您所期望的那样......),那么错误在ruby评估to
参数(raise_error
匹配器)之前引发,因此RSpec无法检查匹配器以查看是否支持值或阻止期望。
答案 2 :(得分:2)
简而言之:
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
答案 3 :(得分:1)
在用括号写的测试中,代码正常执行,包括所有正常的错误处理。花括号语法定义了一个块对象,您可以在其上放置期望值。它封装了您希望被破坏的代码,并允许rspec捕获错误并提供自己的处理(在这种情况下,成功的测试)。
您也可以这样考虑:使用括号,代码在传递给expect
方法之前执行,但是使用块,expect
将运行代码本身。< / p>