btakita / rr

RR (Double Ruby) is a test double framework that features a rich selection of double techniques and a terse syntax.
http://github.com/rr/rr
MIT License
500 stars 58 forks source link

Mocking a method exactly should not prevent the method from being called with other arguments #11

Open lawrencepit opened 15 years ago

lawrencepit commented 15 years ago

Using rails 2.3.3 and rspec 1.2.6, my view is simply this:

  = render :partial => "layouts/resources"

My rspec test looks like this:

  require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')

  describe "/holidays/index.html.haml" do
    it "should render the resources layout partial" do
      # RSpec: works
      #template.should_receive(:render).with(:partial => "layouts/resources")

      # Mocha: fails
      #template.expects(:render).with(:partial => "layouts/resources")

      # RR: fails
      mock(template).render(:partial => "layouts/resources")

      render "/holidays/index.html.haml"
    end
  end

Which results in:

    On subject #<ActionView::Base:0x304b4bc>,
    unexpected method invocation:
      render({:locals=>{}, :layout=>nil, :file=>"holidays/index.html.haml"})
    expected invocations:
    - render({:partial=>"layouts/resources"})

Any advice?

btakita commented 15 years ago

It looks like there are multiple calls to render.

Try: stub(template).render.verbose mock(template).render(:partial => "layouts/resources")

lawrencepit commented 15 years ago

The output I get is then:

    render({:layout=>nil, :file=>"holidays/index.html.haml", :locals=>{}})

    should render the resources layout partial
    render({:partial=>"layouts/resources"})
    Called 0 times.
    Expected 1 times.
thedeeno commented 14 years ago

I can confirm this behavior in rr 0.10.11, rails 2.3.5, rspec 1.3

There is def, as you suggested, a double call to render. This is expected - first the parent view starts rendering, then the child (or partial) view renders. 2 calls to template.render

Rspec mocks can handle this scenario just fine (as noted by OP). Even more confusing is the fact that most examples in the readme deal with the 'doubling' of render(:partial) yet I can't get this to work in view specs at all. Thoughts on how we can set rr up to do this? Any work around? I've tried numerous combinations/setups with no luck.

Regardless thanks for the cool framework. I'm really hoping to switch to this syntax. It's so nice.

thedeeno commented 14 years ago

I dove into the source and found a solution to this. You can write the test as follows:

mock.proxy(template).render(anything).any_number_of_times
render
template.should have_received({:partial => "layouts/resources"})

I was hoping to be able to use spies (since they seem more appropriate here) but there is a catch the following passes ok but when it's intentionally broken rspec hangs.

spy(template)
render
template.should have_received.render({:partial => 'layouts/resources'})

I'll open a new issue to get to the bottom of that one.

mcmire commented 11 years ago

I'll go ahead and mark this as a feature. If you mock a method exactly then it should not prevent the method from being called with other arguments.