When to use curly braces vs parenthesis in expect Rspec method?
In response to OP's comment, I've edited and completely rewritten my answer. I realize that my original answer was oversimplified, so much so that it could be considered incorrect.
Your question was actually addressed somewhat by this other StackOverflow question.
One poster, Peter Alfvin, makes a good point when he says:
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.
The reason you're encountering the phenomenon you're seeing has to do with the raising of errors. When you pass @parser.parse('adsadasdas')
as an argument (use parentheses) to expect
, you are essentially telling ruby:
- Evaluate
@parser.parse('adsadasdas')
first. - Take the result and pass this to
expect
. expect
should see if this result matches my expectation (that is, thatErrno:ENOENT
will be raised).
But, what happens is: when ruby evaluates @parser.parse('adsadasdas')
, an error is raised right then and there. Ruby doesn't even get a chance to pass the result on to expect
. (For all we care, you could have passed @parser.parse('adsadasdas')
as an argument to any function... like multiply()
or capitalize()
) The error is raised, and expect
never even gets a chance to do its work.
But when you pass @parser.parse('adsadasdas')
as a proc (a code block) to expect
using curly braces, what you are telling ruby is this:
expect
, get ready to do some work.expect
, I would like you to keep track of what happens as we evaluate@parser.parse('adsadasdas')
.- Ok,
expect
, did the code block that was just evaluated raise aErrno:ENOENT
error? I was expecting that it would.
When you pass a code block to expect
, you are telling expect
that you want it to examine the resulting behavior, the changes, made by your code block's execution, and then to let you know if it meets up to the expectations that you provide it.
When you pass an argument to expect
, you are telling ruby to evaluate that argument to come to some value before expect
even gets involved, and then you are passing that value to expect
to see if it meets up to some expectation.
Can's use curly braces for it blocks in rspec?
Since do
and end
are keywords which are not used in any other context except for blocks, the Ruby interpreter doesn't have trouble understanding it. But curly braces are used in at least two different contexts, firstly acting as block delimiters, and secondly acting as delimiters for Hash
, which means you have to help the Ruby interpreter a little bit here when using the curly braces to let it know what context you want to use the curly braces in.
In your case, if you use the method syntax with parentheses, i.e. it()
instead of it
, this should tell the interpreter that the curly braces after the parentheses are meant to be interpreted as delimiters for a block.
Use this...
it('returns true for correct password') { expect ... }
instead of this.
it 'returns true for correct password' { expect ... }
Rspec: expect vs expect with block - what's the difference?
As has been mentioned:
expect(4).to eq(4)
This is specifically testing the value that you've sent in as the parameter to the method. When you're trying to test for raised errors when you do the same thing:
expect(raise "fail!").to raise_error
Your argument is evaluated immediately and that exception will be thrown and your test will blow up right there.
However, when you use a block (and this is basic ruby), the block contents isn't executed immediately - it's execution is determined by the method you're calling (in this case, the expect
method handles when to execute your block):
expect{raise "fail!"}.to raise_error
We can look at an example method that might handle this behavior:
def expect(val=nil)
if block_given?
begin
yield
rescue
puts "Your block raised an error!"
end
else
puts "The value under test is #{val}"
end
end
You can see here that it's the expect
method that is manually rescuing your error so that it can test whether or not errors are raised, etc. yield
is a ruby method's way of executing whatever block was passed to the method.
Ruby, rspec Expect error when testing method
When writing a spec that checks for raise_error
, you must use the block-syntax {}
for expect
:
expect { my_method(p1, p2, p3, p4, bad5) }.to raise_error(ArgumentError)
There is a much more in-depth explanation in this question's answers, but you asked for an explanation so I'll give you an abbreviated one:
Most rspec matchers are value matchers. That means that something like this:
expect(some_method).to eq(value)
is really saying:
perform
some_method
and get its return value, then compare its return value tovalue
, and judge the success or failure of this spec on that comparison
When you're trying to test some piece of code that has a side effect, like possibly raising an exception, rspec needs to receive a block of code to run. It's a bit more like:
expect { <some block of code> }.to raise_error(ArgumentError)
perform
<some block of code>
, and then compare what happens when that code is executed to what is expected to happen, and judge the success or failure of this spec on that comparison
This answer for the above-linked question goes into detail about when you need to use expect {}
and when you need to use expect()
.
One-liner shoulda syntax using braces
This is valid Ruby syntax. Well, sort of. It just doesn't make sense!
Since the precedence of a literal block using curly braces is higher than passing an argument without parentheses, the block gets bound to the argument instead of the method call. If the argument is itself a method call, then you won't even get a syntax error. You'll just scratch your head wondering why your block doesn't get called.
To fix this, you either put parentheses around the argument, since parentheses have higher precedence than curly braces, or use the do
/ end
form, which is lower precedence than an argument list without parentheses.
def foo; yield if block_given?; 'foo' end
puts foo { puts 'block' }
# block
# foo
puts(foo) { puts 'block' }
# foo
puts foo do puts 'block' end
# foo
puts foo { puts 'block' }, foo { puts 'block' }
# block
# block
# foo
# foo
puts 'foo' { puts 'block' }
# SyntaxError: (irb):19: syntax error, unexpected '{', expecting $end
rspec expect throws undefined method
It should be @acc.balance
it "should deposit twice" do
@acc = Account.new
@acc.deposit(75)
@acc.balance.should == 75
expect {
@acc.deposit(50)
}.to change(@acc, :balance).to(125)
end
Rspec - Testing uniqueness on a has_many through
expect
with raise should be wrapped in a block
expect(@school.tags << @tag1).to raise_error(ActiveRecord::RecordInvalid)
should be
expect{@school.tags << @tag1}.to raise_error(ActiveRecord::RecordInvalid)
Related Topics
Access Instance Variable from Outside the Class
What's the Difference Between Process.Fork and Process.Spawn in Ruby 1.9.2
How to Use Jquery-Tokeninput and Acts-As-Taggable-On
How to Check Whether a Value in a String Is an Ip Address
How to Use Xmlns Declarations with Xpath in Nokogiri
Is It a Bad Practice to List Ruby Version in Both Gemfile and .Ruby-Version Dotfile
Custom Ruby Gem in Gemfile on Heroku
One Liner in Ruby for Displaying a Prompt, Getting Input, and Assigning to a Variable
How to Track Down a Memory Leak in My Ruby Code
How to Find If a String Starts with Another String in Ruby
Extracting the Last N Characters from a Ruby String
How to Avoid Putting the Magic Encoding Comment on Top of Every Utf-8 File in Ruby 1.9
Options for Distribution of an Offline Ruby on Rails Application
Differences Between Proc and Lambda
How to Get Attributes That Were Defined Through Attr_Reader or Attr_Accessor