On Sun, Sep 28, 2008 at 10:43 AM, David C. [email protected]
wrote:
}.should change { Story.unposted }.from([@story]).to([])
response to == and inspect.
I’m looking at seeing if there’s a way we can make “should change”
work in spite.
Wow.
OK - here’s what I figured out. Talk about insidious bugs! This is
actually quite a bit different from what I thought.
There are two lambdas involved here:
lambda {
1st lambda: expression that should cause the change
}.should change{
2nd lambda: expression that returns the object that should change
}.to(expected value)
The matcher executes the 1st lambda and stores the result as @before.
In your example this is a Rails association proxy for the
Story.unposted collection.
The matcher then executes the 2nd lambda.
The matcher then executes the 1st lambda again and stores the result
as @after. In your example, this is, again, a Rails association proxy
for the Story.unposted collection.
At this point, @before and @after point to the same object - the same
Rails association proxy!!!
The matcher passes if @before != @after and fails if @before ==
@after. Because they are actually the same association proxy, the
example fails.
Now rspec asks the matcher to print out the reason why it failed,
which does this:
“#{result} should have been changed to #{@to.inspect}, but is now
#{@after.inspect}”
@to is the expected value []
@after is the association proxy, which proxies to an empty collection.
Now, when the matcher calls @after.inspect, is the first time that the
proxy is actually evaluated!!!
Does this make sense?
I was able to get a similar example to pass by doing this immediately
after storing the proxy in the @before variable:
@before = @before.collect{|item|item} if @before.respond_to?(:collect)
Ugly, ugly, ugly. But perhaps necessary to deal w/ this problem.
I think I’ll restructure things so the the change matcher handles this
in rails, but not in core rspec.
Thoughts?