Dude! You're still testing the framework!
In my last post, Quit testing the framework!, I talked about my philosophy of testing without second guessing the Rails framework. I'd like to continue the conversation, this time focusing on the controller side of things. If you haven't read the previous post, I suggest you do it now. Otherwise, you might be a tad lost.
Ok, our controller: users_controller.rb
class UsersController < ApplicationController
def index
end
def activate
@user = User.find(params[:id])
@user.activate! if @user
render :action => :index
end
end
We are going to focus on testing the activate action. Specifically, I want to focus on finding and activating the user. I'll save the rest of the specs for the download.
Here is what our initial specs might look like: users_controller_spec.rb
describe UsersController, "responding to POST /activate" do
before(:each) do
@user = User.create(:status => "Some Status")
end
def do_post
post :activate, :id => @user.id
end
it "should find a user" do
do_post
assigns[:user].should_not be_nil
end
it "should activate a user" do
do_post
assigns[:user].should be_active
end
end
Let's look at the first spec. We are creating a post to the activate action and verifying that a user was actually found.
Do we actually need to verify that a User was found? Not really. How about we trust ActiveRecord and concentrate on making sure we call the proper find method. While we are at it, let's get rid of that nasty assigns[:user] junk. Personally, I think assigns should be reserved for confirming that we actually set an instance variable.
A little stubbing, a little mocking , a smidge of refactoring...
describe UsersController, "responding to POST /activate" do
before(:each) do
@user = mock_model(User, :id => "1")
@user.stub!(:activate!)
User.stub!(:find).and_return(@user)
end
def do_post
post :activate, :id => @user.id
end
it "should find a user" do
User.should_receive(:find).with(@user.id)
do_post
end
end
Okay, that feels a little better to me. We are just making sure that User.find is being called properly. Notice that we've stubbed User.find. As long as Rails is doing it's job, we shouldn't have to worry about testing the result of a call to find. A nice side effect is that we are no longer creating and destroy an actual database model for each spec.
Now that we've focused on finding the user, let's turn our attention to actually activating the user. Our initial spec was a lil' ikky. We still had that ugly assigns call. And, besides, we've already tested the activate! method back when we tested our model. How about we just make sure that activate! is called? Sounds good to me.
A bit more refactoring:
it "should activate a user" do
@user.should_receive(:activate!)
do_post
end
Ahoy! New hotness! Just make sure that activate! was called. Leave the rest of the details to our model spec.
And our testing rundown:
| Method | Tested |
|---|---|
| User.find was called | users_controller_spec.rb |
| @user.activate! was called | users_controller_spec.rb |
| @user.activate! implementation | user_spec.rb |
| @user.active? | user_spec.rb |
| @user.update_attribute | Rails framework |
I've included the user model, controller and controller spec for your here: user.rb, users_controller.rb, users_controller_spec.rb
If you'd like the user model spec, it was included in my previous post.
As always, I welcome any and all comments. Please see the sidebar for instructions on sending me a message.
Enjoy!