What does using a lambda in rspec tests accomplish?

Here is the code in question:

describe UsersController do
render_views

describe “POST to ‘create’ action” do
describe “failure” do
before(:each) do
@attr = { :name => ‘’, :email => ‘’, :password => ‘’,
:password_confirmation => ‘’ }
end

  it "should not create a user" do
    lambda {
      post :create, :user => @attr
    }.should_not change(User, :count)
  end

  it "should have the right title" do
    post :create, :user => @attr
    response.should have_selector(:title, :content => 'Sign up')
  end

Comparing the last two tests, it looks to me like:

   lambda {
      post :create, :user => @attr
    }.should_not change(User, :count)

should be equivalent to:

    post :create, :user => @attr
    response.should_not change(User, :count)

But when I make that change, the test fails with this error:

Failures:

  1. UsersController Post create failure should not create a user
    Failure/Error: response.should_not change(User, :count)
    NoMethodError:
    undefined method `call’ for
    #ActionController::TestResponse:0x00000100f3d3a0

    ./spec/controllers/users_controller_spec.rb:62:in `block (4

levels) in <top (required)>’

Okay, so it looks like should_not() is probably defined something like
this:

def should_not(meth)
before = meth()
self.call
after = meth()

before == after
end

…and because a response object is not necessarily something you can
call, self.call, which is equivalent to response.call, caused the error.

But then I see should_not used like this:

it “should set the encrypted password” do
@user.encrypted_password.should_not be_blank
end

??? In that case, should_not is being called on a String, and ruby
Strings can’t be call()'ed.

First, the test fails because it expects a Proc – it’s trying to
invoke #call. So you can’t do what you’re proposing without writing a
new definition for how #change works.

As a deeper answer, your “shouldn’t these be equivalent?” statement is
not accurate. RSpec, like any piece of Ruby code, needs to know when
it should start paying attention for the change(). In other words, it
needs a “before” to start with and an “after” to compare to. With a
lambda, this is accomplished by measuring the thing you want to check
before the lambda is run, and then again after it’s run. If the
difference matches what you expected, the test passes. If it doesn’t
it fails.

Your example is asking RSpec to do this:

before.val = 5
before.val = 6
before.should change(:val).from(5).to(6)

How can it know that before.val used to be 5? Answer: it can’t. By
the time you reach the third line, the old value of before.val is
gone forever.

~ jf

John F.
Principal Consultant, BitsBuilder
LI: http://www.linkedin.com/in/johnxf
SO: User John Feminella - Stack Overflow

John F. wrote in post #1012869:

In other words, it
needs a “before” to start with and an “after” to compare to. With a
lambda, this is accomplished by measuring the thing you want to check
before the lambda is run, and then again after it’s run.

Okay, I sort of sussed that out myself. But the question remains, how
can should_not() expect a lambda in one case, and then successfully
operate on a String in another case, like here:

@user.encrypted_password.should_not be_blank

Does the definition of should_not check the type of the receiver before
executing? I can’t find any docs for the definition of should_not().

On Jul 25, 6:40pm, 7stud – [email protected] wrote:

@user.encrypted_password.should_not be_blank

Does the definition of should_not check the type of the receiver before
executing? I can’t find any docs for the definition of should_not().

should and should_not are both added to every object, and pass self
(the receiver) to the matches?() method on the matcher passed to
should[_not]. e.g.

obj.should matcher # => matcher.matches?(obj)

It is the matcher that cares what the object is, not the should[_not]
method. And the matchers do not, generally, do type checking on the
object - they just assume that you’re doing the right thing, and let
Ruby handle errors if you do the wrong thing (e.g. undefined method
‘call’).

HTH,
David

David C. wrote in post #1013001:

On Jul 25, 6:40pm, 7stud – [email protected] wrote:

@user.encrypted_password.should_not be_blank

Does the definition of should_not check the type of the receiver before
executing? I can’t find any docs for the definition of should_not().

should and should_not are both added to every object, and pass self
(the receiver) to the matches?() method on the matcher passed to
should[_not]. e.g.

obj.should matcher # => matcher.matches?(obj)

It is the matcher that cares what the object is, not the should[_not]
method.

Thanks for that. I was able to find some more details based on your
post. The lambda construct in rspec can be simplified like this:

a_function.should_not( change(User, :count ) )

And should_not() is defined something like this:

def should_not(obj) #change() returns an object–see details below
result = obj.matches?(self)


end

The change() method simply returns a Change ‘matcher’ object:

def change(a_class, message) #(User, :count)
Change.new(a_class, message) #(User, :count)
end

where the Change class is defined something like this:

class Change
def initialize(a_class, message) #(User, :count)
@receiver = a_class
@msg = message
end

def matches?(callable)  #(a_function)
  before = @receiver.send(msg)  #same as calling User.count()
  callable.call        #execute a_function()
  after = @receiver.send(msg) #same as calling User.count()

  before == after
end

...
...

end

For references, see:

  1. http://rspec.rubyforge.org/rspec/1.1.9/classes/Spec/Expectations.html

====
Spec::Expectations adds two methods to Object:

should(matcher=nil)
should_not(matcher=nil)

Both methods take an optional Expression Matcher (See Spec::Matchers).

When should receives an Expression Matcher, it calls matches?(self)**.
If it returns true, the spec passes and execution continues. If it
returns false, then the spec fails with the message returned by
matcher.failure_message.

Similarly, when should_not receives a matcher, it calls matches?(self).
If it returns false, the spec passes and execution continues. If it
returns true, then the spec fails with the message returned by
matcher.negative_failure_message.

RSpec ships with a standard set of useful matchers, and writing your own
matchers is quite simple. See Spec::Matchers for details.

**That can be made more clear:

When should() receives an Expression Matcher, it calls
expr_matcher.matches?(self):

def should(expr_matcher)
result = expr_matcher.matches?(self)


end

self is the object calling the should() method.

If matches?() returns true, the spec passes and execution continues. If
it returns false, then the spec fails with the message returned by
expr_matcher.failure_message.

http://www.robertsosinski.com/2008/12/10/up-and-running-with-custom-rspec-matchers/

However, that tutorial contains this passage:

===
3. Within the heart of our matcher is the matches? method. This method
takes one parameter, the actual value of the object that the should or
should_not method is passed to within your RSpec test.

That sentence is faulty ruby speak, and therefore doesn’t make any
sense. It should say,

===
3.
Within the heart of our matcher is the matches? method.
This method takes one parameter, the actual value of the object that the
should or should_not message is passed to within your RSpec test.

But in ruby, that simply means one thing:

===
Within the heart of our matcher is the matches? method.
This method takes one parameter: the receiver of the should() or
should_not() method call (where the ‘receiver’ is the thing that is
calling the should() or should_not method).

Whoops. s/msg/@msg/g

This:

===
where the Change class is defined something like this:

class Change
def initialize(a_class, message) #(User, :count)
@receiver = a_class
@msg = message
end

def matches?(callable)  #(a_function)
  before = @receiver.send(msg)  #same as calling User.count()
  callable.call        #execute a_function()
  after = @receiver.send(msg) #same as calling User.count()

  before == after
end

...
...

end

should look more like this:

===
where the Change class is defined something like this:

class Change
def initialize(a_class, message) #(User, :count)
@receiver = a_class
@msg = message
end

def matches?(callable)  #(a_function)
  before = @receiver.send(@msg)  #same as calling User.count()
  callable.call        #execute a_function()
  after = @receiver.send(@msg) #same as calling User.count()

  before == after
end

...
...

end

Similarly, calling should_not on a string works like this:

string.should_not(be_blank)

def should_not(obj) #be_blank() returns an object–see details below
result = obj.matches?(self)


end

def be_blank
Blank.new
end

class Blank
def matches?(string)
@actual = string

   string.blank?
 end

 ...
 ...

end

Note that be_blank() accepts no arguments.

As David C. pointed out, it is the change() method and the
be_blank() method that care about the type of the object that is used to
call should() and should_not() (the ‘receiver’). The matcher object
uses
the receiver as if it is the type the matcher object expects (a callable
in the case of change(), and a String in the case of be_blank). If the
receiver isn’t of the expected type, then ruby throws an error when the
matcher object calls unknown methods on the receiver.