Post Meta

Bookmarks

  • Delicious
  • Digg
  • Reddit
  • Magnolia
  • Newsvine
  • Furl
  • Facebook
  • Technorati

Testing controllers let me understand what are mocks and stubs.

The basic idea in RSpec controller testing is that controllers are fully separated from models, and views. (You can integrate_views in controller testing, but this is out of scope now).

Since there is no model, there is no database (test database) and there are no fixtures. So how you can test controllers where the business logic / controller logic deals with data from database?

Let’s see the first method which will be tested:

def index
    if not logged_in?
      if User.count > 0
        redirect_to(:action => 'login')
      else
        # first user must be added manually
        render :text => l(:no_user_defined)
      end
    end
  end

As you can see, if there are users in the db (User.count >0) a login screen will be shown, else just a message is rendered. To tell RSpec about this condition we must use stubs.

Stubs are fake methods associated to models. In this case we will have to fake count to be > 0 by using this code:

User.stub!(:count).and_return(1)

To understand better let’s see another method, the login process:

def login    
    return unless request.post?      
    self.current_user = User.authenticate(params[:login], params[:password])
    if logged_in?           
      redirect_back_or_default(:controller => 'welcome')
      flash[:notice] = l(:account, :logged_in_successfully) 
    else
      flash[:notice] = l(:account, :invalid_username_or_password)
    end
  end

Here we must use User.authenticate() to check if the login is successful. We can fake out by the following stub:

User.stub!(:authenticate).and_return(1)

And to put in context, I’ll copy here the full RSpec and the full output. You’ll see how easy is to test the controller when there are no users defined, and when there are users.

RSpec

describe AccountController do
 
  describe "showing up the login screen" do
 
    it "should start with /index" do      
      get :index
      response.should be_success
    end
 
    it "should render message '#{l(:no_user_defined)}' if there are no users in the system defined" do
      get :index
      response.should have_text(l(:no_user_defined))
    end
  end
 
 
  describe "logging in" do
 
    before(:each) do      
      User.stub!(:count).and_return(1)      
    end
 
    it "should redirect to /login if there are users" do     
      get :index
      response.should redirect_to(:action => 'login')
    end
 
    it "should redirect to /welcome if login is successful and say '#{l(:account, :logged_in_successfully)}'" do   
      User.stub!(:authenticate).and_return(1)
      post :login, :login => "test", :password => "test"      
      response.should redirect_to(:controller => "welcome")  
      flash[:notice].should have_text(l(:account, :logged_in_successfully))
    end
 
    it "should say '#{l(:account, :invalid_username_or_password)}' if login is not successful" do         
      post :login, :login => "test", :password => "test"     
      flash[:notice].should have_text(l(:account, :invalid_username_or_password))
    end
  end

Output

AccountController showing up the login screen
- should start with /index
- should render message 'Nu exista nici un utilizator definit in acest sistem. <br/> Va rugam contactati administratorul de sistem.' if there are no users in the system defined
 
AccountController logging in
- should redirect to /login if there are users
/home/cs/workspace/t2/config/../lib/authenticated_system.rb:16: warning: Object#id will be deprecated; use Object#object_id
- should redirect to /welcome if login is successful and say 'Autentificare cu success'
- should say 'Nume utilizator sau parola incorecta' if login is not successful


Related posts

Leave A Comment

+ -