Stubbing the User Model

July 3, 2010
shoulda tdd authlogic rails3 mocha

I've been working a lot these days on testing. I try my best to follow a TDD/BDD workflow when developing applications, but I still have bad habits left over from the PHP days where I will start working on something, realize I need something else, and then something else, and before you know it, I am way behind on tests.

Before, I was relying completely on testing with Cucumber, but after speaking to a lot of people at Railsconf, it seemed that a lot of people were using Test::Unit and Shoulda. I decided to give them a try here. I also used Factory Girl as a fixture replacement, like I had done in the past with some other problems.

The project I am working on is a host tracking system. Basically the main model is Host, and the frontline controller is HostsController.

For the Host model, I didn't do anything too outside of what is in the Shoulda README, but the controller took some work. I have a custom written authentication gem that basically just includes the recommended methods (require_user, current_user, logged_in?, etc..) into ApplicationController or whichever controller that needs them, since I am aiming for a single-sign-on type system, I use the same session secret for all applications, and the User model reads from a central database using a separate "establish_connection" call at the top of the model. The database is for the User model that managed through another application.

app/models/user.rb

class User < ActiveRecord::Base
  establish_connection(
    YAML.load_file("#{Rails.root}/config/sessions_database.yml")[Rails.env]
  )
end


I want the entire application to be protected, so I use the before filter to require a user (which looks at the current_user method), if current_user is not set to a valid User, they're redirected to the other application that will log them in and set the proper cookies using LDAP and Authlogic (this will require another post sometime.)

app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  include Esc::Authentication

  before_filter :require_user
  before_filter :set_user
end


But since I call an external database, the User isn't getting created properly when setting up the databases for testing. I also have my own tests on the other application to validate that people are logging in properly and that everything is working there. I'd like to just make the assumption that the user is logged in. The absolute easiest way to do this, is to just use mocha to make current_user always return the Factory that was created for my user.

test/factories/users.rb
Factory.define :admin_user, :class => "user" do |f|
  f.email "daniel.mcnevin@something.com"
  f.first_name "Daniel"
  f.last_name "McNevin"
end


test/functional/hosts_controller_test.rb
require 'test_helper'

class HostsControllerTest < ActionController::TestCase
  context "Logged in as admin" do
    setup do
      @user = Factory.build(:admin_user) ## build, not create since it won't hit the database then
      HostsController.any_instance.stubs(:current_user).returns(@user)
    end
  
    context "on INDEX action" do
      setup do
        get :index
      end

      should respond_with(:success)
      should render_template(:index)
      should respond_with_content_type(:html)    
    end
      
  end
end


From there, I will be able to start building my functional tests, using different User factories (admin user, normal user, read-only user) and make sure that I am getting the proper result.