Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is going on here: rspec stub(:new).with...?

I'm a little confused about what is going on with the scaffold controller specs that rspec generates. It seemed to be making sense until I added authorization to my app and now I need to update my tests.

MyClass.stub(:new).with('these' => 'params') { mock_my_class(:save => true) }

In my controller I merge a hash into params when creating a new record (it needs the current_user id to be valid). MyClass.new(params[:my_class].merge(:user_id => current_user.id))

Test Fails

expected: ({"these"=>"params"})
got: ({"these"=>"params", "user_id"=>315})

It makes sense that the test fails because the new method receives params it didn't expect. It expected to receive {'these' => 'params'} but it actually received {'these' => 'params', 'user_id' => 1234}

So my natural reaction is to adjust the test because the new method should receive {'these' => 'params', 'user_id' => 1234} and return the mock object.

So I add to the test as follows:

   MyClass.stub(:new).with({'these' => 'params', 'user_id' => @user.id}) { mock_my_class(:save => true) }   

Here is where I get thrown through a loop. The output of the test is as follows:

expected: ({"these"=>"params", "user_id"=>298})
got: ({"these"=>"params"})

It seems as if a successful test is magically evading me. I'm sure there is a logical reason for these results, but I can't seem to figure them out.

Any help? :)

note:

The rspec site says the following:

Account.should_receive(:find).with("37").and_return(account)

or

Account.stub!(:find).and_return(account)

This is easy enough to follow it just seems odd the the scaffold generated would not contain these methods (unless I botched something which is possible (: )


Passes

login_admin
describe "with valid params" do
  it "assigns a newly created forum_sub_topic as @forum_sub_topic" do
    ForumSubTopic.stub(:new) { mock_forum_sub_topic(:save => true) }
    ForumSubTopic.should_receive(:new).with({"these"=>"params", "user_id"=> @admin.id})  #PASS!
    post :create, :forum_sub_topic => {'these' => 'params'}
    assigns(:forum_sub_topic).should be(mock_forum_sub_topic) #PASS!
  end
end

Fails

login_admin
describe "with valid params" do
  it "assigns a newly created forum_sub_topic as @forum_sub_topic" do
    ForumSubTopic.stub(:new).with({'these' => 'params', 'user_id' => @user.id}) { mock_forum_sub_topic(:save => true) } 
    post :create, :forum_sub_topic => {'these' => 'params'}
    assigns(:forum_sub_topic).should be(mock_forum_sub_topic)
  end
end
like image 392
recursive_acronym Avatar asked May 16 '11 03:05

recursive_acronym


1 Answers

"Never trust a junkie", as the saying goes. One could also say, "never trust a scaffold".

OK, that's being a little bit too harsh. The scaffold does its best to figure out which parameters will work for the models/controllers you are generating, but it doesn't know about nested resources (which is what I assume you are using), so it won't generate the user_id in the params hash. Add that:

post :create, :forum_sub_topic => {:user_id=>@user.id}

The these_params key is generated as an example — remove it and add whatever parameters are needed for the controller to create a MyClass.

Regarding the with option: stub and should_receive will only stub out messages that meet the specified conditions, i.e. if you do:

MyClass.stub(:new) {mock_model(MyClass,:save=>true)}

Then MyClass will respond to any new message with the mock. If, on the other hand, you do:

MyClass.stub(:new).with({:bogus=>37}) {mock_model(MyClass,:save=>true)}

Then MyClass will only respond to new when it also receives {:bogus=>37} as an argument.

like image 173
zetetic Avatar answered Nov 13 '22 05:11

zetetic