[rspec-devel] Fwd: [#11628] error mocking partial page variable only after partial has already been rendered bug

aslak hellesoy aslak.hellesoy at gmail.com
Fri Jun 22 10:40:29 EDT 2007

Forwarding this to the list as it has general interest.

---------- Forwarded message ----------
From: David Chelimsky <dchelimsky at gmail.com>
Date: Jun 22, 2007 3:56 PM
Subject: Re: [#11628] error mocking partial page variable only after
partial has already been rendered bug
To: Brian Takita <brian.takita at gmail.com>
Cc: aslak hellesoy <aslak.hellesoy at gmail.com>

The problem that we're trying to solve is providing stub values to
partials. Doing that using the mocking framework does not work because
the templates get compiled.

An approach that works is this (directly in the example):

render :partial => "people/person", :object => @person

Here's where this starts to break down. Let's say that
_person.html.erb is calling _address.html.erb with it's person object.
In the example, I want to do this:

template.should_receive(:render).with(:partial => "address", :person => @person)
render :partial => "people/person", :object => @person

As you might imagine, this breaks down because rails makes use render
as an entry point for everything, so the call to render :partial =>
"people/person" gets compared to the expectation and fails - nothing
ever gets rendered at all because we've set a message expectation on

So what if we added special high level methods to ActionView::Base
rather than trying to intercept the deeper calls. Something like:

template.should_receive_render_with(:partial => "address", :person => @person)
render :partial => "people/person", :object => @person

The should_receive_render_with method would intercept calls to render
and compare them to the expectation. If they meet the expectation this
gets recorded. If it's the right template with the wrong hash, it
fails immediately. Otherwise it passes the call back to the original
render method. We can also have it add itself as a listener to the
runner, so when the example finishes it can raise an
ExpectationNotMetError if it never got the message.

The benefit is that you can focus on one template and essentially mock
all of it's included partials. The downside is that if a given partial
is invoked more than once, you'd have to be able to set that
expectation, either with .once, .twice, .exactly(n).times appended to
the expectation, or with repeated expectations if you want to be
specific about different hash values.

Maybe to make it more clear the method should be:

template.should_render_partial(:address).with(:object => @person)

That separates the partial name, which is essentially our key as to
whether or not to pay attention to the message, from the other
arguments we need to match against.

The downside is that we essentially have to re-implement a lot of what
mocks already do - or extract some of that stuff out to another
library - in order to provide a "pass through" feature, which a few
people have asked for and I'm loathe to add to the mocking framework.



On 6/19/07, Brian Takita <brian.takita at gmail.com> wrote:
> Hello,
> I looked into this a little.
> It looks like in the success case, the compiled template source is evaled:
> CompiledTemplates.module_eval(render_source, file_name, -line_offset)
> in line 518.
> Since person is stubbed in the template object, the eval can't find
> the local variable and falls back to the person method.
> On the failure case, @@template_args[render_symbol] is set in the first spec.
> This causes
> person_counter = local_assigns[:person_counter]\nperson = local_assigns[:person]
> to be compiled, which sets person to nil.
> There are a couple of solutions:
> Option 1:
> ActionView::Base#compile_template? must return true for every render
> (line 317 in base.rb) to recompile the template method.
> @@template_args[render_symbol] must be cleared before the template is compiled
> Option 2:
> ActionView::Base#render_template must get the locals_assigns hash somehow.
> Both seem pretty hairy.
> Brian

More information about the rspec-devel mailing list