[rspec-users] Odd parameter munging with with()

David Chelimsky dchelimsky at gmail.com
Tue Feb 20 11:37:18 EST 2007


On 2/20/07, David Chelimsky <dchelimsky at gmail.com> wrote:
> On 2/20/07, David Chelimsky <dchelimsky at gmail.com> wrote:
> > On 2/20/07, Jerry West <jerry.west at ntlworld.com> wrote:
> > >
> > >
> > >
> > >  So, I still say my app code and my reduction look conceptually the
> > > same, but I'm stumped as to why they don't behave the same way
> > >
> > >  David seems to have picked that up.  Ignore my blatherings about
> > > HashArgConstraint (though the rest is valid).
> > >
> > >  Here's another twist under  Rails 1.2.2 (which may not be supported but
> > > works fine for what I am doing so far).
> > >
> > >  Basically, a different expectation seems to be set up depending on whether
> > > the argument to get/post is a variable or not.  That cannot be right.  What
> > > am I missing?
> > >
> > >  require File.dirname(__FILE__) + '/../spec_helper'
> > >
> > >  class TestController < ActionController::Base
> > >    def index() self.amethod(params) end
> > >  end
> > >
> > >  context 'a test controller' do
> > >    controller_name :test
> > >
> > >    setup do
> > >      @plist = { "id" => "1", "param2" => "bar" }
> > >
> > > controller.should_receive(:amethod).with(@plist).and_return(true)
> > >    end
> > >
> > >   specify "should pass a hash to #with (1)" do
> > >      get :index, "id" => "1", "param2" => "bar"
> > >    end
> > >    specify "should pass a hash to #with (2)" do
> > >      get :index, { "id" => "1", "param2" => "bar" }
> > >    end
> > >    specify "should pass a hash to #with (3)" do
> > >      get :index, @plist
> > >    end
> > >  end
> > >
> > >  $ spec -cfs spec/controllers/test_controller_spec.rb
> > >  /usr/bin/spec:17:Warning: require_gem is obsolete.  Use gem instead.
> > >  /home/jjw/rails/playground/config/boot.rb:29:Warning:
> > > require_gem is obsolete.  Use gem instead.
> > >
> > >  a test controller
> > >  - should pass a hash to #with (1) (FAILED - 1)
> > >  - should pass a hash to #with (2) (FAILED - 2)
> > >  - should pass a hash to #with (3) (FAILED - 3)
> > >
> > >  1)
> > >  'a test controller should pass a hash to #with (1)' FAILED (ignore the
> > > failure, look at the expectation)
> > >  #<TestController:0xb70a1df8> expected :amethod with (["param2", "bar"],
> > > ["id", "1"]) but received it with ({"action"=>"index", "id"=>"1",
> > > "param2"=>"bar", "controller"=>"test"})
> > >  (eval):3:in `amethod'
> > >  ./spec/controllers/test_controller_spec.rb:4:in `index'
> > >  /usr/local/lib/site_ruby/1.8/benchmark.rb:300:in `measure'
> > >  ./spec/controllers/test_controller_spec.rb:13:
> > >
> > >  2)
> > >  'a test controller should pass a hash to #with (2)' FAILED (ignore the
> > > failure, look at the expectation)
> > >  #<TestController:0xb7093820> expected :amethod with (["param2", "bar"],
> > > ["id", "1"]) but received it with ({"action"=>"index", "id"=>"1",
> > > "param2"=>"bar", "controller"=>"test"})
> > >  (eval):3:in `amethod'
> > >  ./spec/controllers/test_controller_spec.rb:4:in `index'
> > >  /usr/local/lib/site_ruby/1.8/benchmark.rb:300:in `measure'
> > >  ./spec/controllers/test_controller_spec.rb:18:
> > >
> > >  3)
> > >  'a test controller should pass a hash to #with (3)' FAILED (ignore the
> > > failure, look at the expectation)
> > >  #<TestController:0xb708cf34> expected :amethod with ([:only_path, true],
> > > ["param2", "bar"], ["id", "1"], [:action, :index]) but received it with
> > > ({"action"=>"index", "id"=>"1", "param2"=>"bar", "controller"=>"test"})
> > >  (eval):3:in `amethod'
> > >  ./spec/controllers/test_controller_spec.rb:4:in `index'
> > >  /usr/local/lib/site_ruby/1.8/benchmark.rb:300:in `measure'
> > >  ./spec/controllers/test_controller_spec.rb:23:
> > >
> > >  Finished in 0.022612 seconds
> > >
> > >  3 specifications, 3 failures
> > >
> > >
> > >  Where the heck did the :only_path stuff come from in the expectation for
> > > case 3??  They weren't there in ArgumentExpectation#initialize.
> > >
> > >  As expected, creating the 'right' expectation  (@plist = { "id" =>
> > > "1","param2" => "bar", "action" => "index", "controller" => "test" } )
> > > allows the spec to pass in cases 1 & 2, but #3 still fails.
> >
> > Here's the same output w/ the latest trunk. Note that it shows #with
> > correctly receiving a Hash now.
> >
> > 1)
> > Spec::Mocks::MockExpectationError in 'a test controller should pass a
> > hash to #with (1)'
> > #<TestController:0x2578854> expected :amethod with ({"param2"=>"bar",
> > "id"=>"1"}) but received it with ({"action"=>"index", "id"=>"1",
> > "param2"=>"bar", "controller"=>"test"})
> > ./spec/controllers/temp_spec.rb:4:in `index'
> > ./spec/controllers/temp_spec.rb:16:
> >
> > 2)
> > Spec::Mocks::MockExpectationError in 'a test controller should pass a
> > hash to #with (2)'
> > #<TestController:0x2516ec4> expected :amethod with ({"param2"=>"bar",
> > "id"=>"1"}) but received it with ({"action"=>"index", "id"=>"1",
> > "param2"=>"bar", "controller"=>"test"})
> > ./spec/controllers/temp_spec.rb:4:in `index'
> > ./spec/controllers/temp_spec.rb:19:
> >
> > 3)
> > Spec::Mocks::MockExpectationError in 'a test controller should pass a
> > hash to #with (3)'
> > #<TestController:0x2461d58> expected :amethod with ({:only_path=>true,
> > "param2"=>"bar", "id"=>"1", :action=>:index}) but received it with
> > ({"action"=>"index", "id"=>"1", "param2"=>"bar",
> > "controller"=>"test"})
> > ./spec/controllers/temp_spec.rb:4:in `index'
> > ./spec/controllers/temp_spec.rb:22:
> >
> > I have NO idea yet about  :only_path. I'll keep you posted...
>
> OK, I now have SOME idea, but I'm still trying to figure out
> specifics. The following passes. Note that it uses @plist.dup each
> time.
>
> require File.dirname(__FILE__) + '/../spec_helper'
>
> class TestController < ActionController::Base
>   def index() self.amethod(params) end
> end
>
> context 'a test controller' do
>   controller_name :test
>
>   setup do
>     @plist = { "id" => "1", "param2" => "bar" , "controller" =>
> "test", "action" => "index" }
>     controller.should_receive(:amethod).with(@plist.dup).and_return(true)
>   end
>
>  specify "should pass a hash to #with (1)" do
>     get :index, "id" => "1", "param2" => "bar"
>   end
>
>   specify "should pass a hash to #with (2)" do
>     get :index, { "id" => "1", "param2" => "bar" }
>   end
>
>   specify "should pass a hash to #with (3)" do
>     get :index, @plist.dup
>   end
> end
>
> If you search through Rails you'll find dozens of instances of
> :only_path. So the problem SEEMS to be that by the time we reach the
> third spec, @plist has been touched by Rails.
>
> BTW - here's a variation that passes and allows you to deal only w/
> the subset of keys you want.
>
> context 'a test controller' do
>   controller_name :test
>
>   setup do
>     controller.should_receive(:amethod).and_return(true) { |params|
>       params["id"].should == "1"
>       params["param2"].should == "bar"
>     }
>   end
>
>  specify "should pass a hash to #with (1)" do
>     get :index, "id" => "1", "param2" => "bar"
>   end
>
>   specify "should pass a hash to #with (2)" do
>     get :index, { "id" => "1", "param2" => "bar" }
>   end
>
>   specify "should pass a hash to #with (3)" do
>     @plist = { "id" => "1", "param2" => "bar" }
>     get :index, @plist.dup
>   end
> end

OK - I figured it out. Because the third spec is passing the actual
Hash instance to "get :index", Rails gets a direct reference to it and
can do what it wants. If you're interested in details in this case,
take a look at test_process.rb:430/431.

So the lesson here is don't use expectation objects as parameters to
your system as the system may produce unwanted side effects.

Cheers,
David



>
> Cheers,
> David
>
> >
> > >
> > >  rgds from "Confused of Hampshire".
> > >
> > >
> > >
> > >
> > > _______________________________________________
> > > rspec-users mailing list
> > > rspec-users at rubyforge.org
> > > http://rubyforge.org/mailman/listinfo/rspec-users
> > >
> >
>


More information about the rspec-users mailing list