Skip to main content

Rails: RSpec, Cucumber, Authlogic, and factory_girl

After a day of work and a week of reading The RSpec Book, I got RSpec, Cucumber, Authlogic, and factory_girl to play nicely with each other. I can now test user registration, logging in, and logging out.

I can't possibly cover everything from scratch, but I'll cover some of the trickier integration points.

Setup Rails, RSpec, Cucumber, and Authlogic per their own documentation. I ended up with the following in both config/environments/test.rb and config/environments/cucumber.rb:
# There is some duplication between test.rb and cucumber.rb.
config.gem "cucumber", :lib => false, :version => ">=0.3.11"
config.gem "webrat", :lib => false, :version => ">=0.4.4"
config.gem "rspec", :lib => false, :version => ">=1.2.6"
config.gem "rspec-rails", :lib => 'spec/rails', :version => ">=1.2.6"
config.gem "thoughtbot-factory_girl", :lib => "factory_girl", :version => ">=1.2.2", :source => "http://gems.github.com"
I created a factory in test/factories/user.rb:
Factory.define :user do |u|
u.username 'john'
u.email 'john@example.com'
u.password 'funkypass'
u.password_confirmation 'funkypass'
u.first_name 'John'
u.last_name 'Smith'
u.billing_address "1 Cool St.\r\nBerkeley"
u.billing_state 'CA'
u.billing_zipcode '94519'
u.other_zipcode '28311'
u.phone '(925) 555-1212'
end
I created a Cucumber feature in features/user_sessions.feature:
Feature: User Sessions

So that I can blah, blah, blah
As a registered user
I want to log in and log out

Scenario: log in
Given I am a registered user
And I am on the homepage
When I login
Then I should see "Login successful!"
And I should see "Logout"

Scenario: log out
Given I am logged in
And I am on the homepage
When I follow "Logout"
Then I should see "Logout successful!"
And I should see "Register"
And I should see "Log In
To implement this, I created step definitions in features/step_definitions/user_sessions_steps.rb
def user
@user ||= Factory :user
end

def login
user
visit path_to("the homepage")
response.should contain("Log In")
click_link "Log in"
fill_in "Username", :with => "john"
fill_in "Password", :with => "funkypass"
click_button "Login"
response.should contain("Login successful!")
response.should contain("Logout")
end

Given /^I am a registered user$/ do
user
end

When /^I login$/ do
login
end

Given /^I am logged in$/ do
login
end
That's it.

Authlogic's testing documentation talks about stuff like:
require "authlogic/test_case"
...
include Authlogic::TestCase
...
setup :activate_authlogic
UserSession.create(users(:whomever)) # logs a user in
In theory, you should be able to log a user in using some code--without going through the login form. I worked on this for several hours, but I wasn't able to get it to work. I googled quite a bit, but I wasn't able to find anyone else who could get this to work (i.e. integrate RSpec, Cucumber, Authlogic, and factory_girl). It seems that everyone else came to the same conclusion I did--just force the tests to use the login form.

Comments

Brandon Rhodes said…
Could you filter your posts so that Ruby-only material does not wind up on Planet Python? Thanks!
jjinux said…
See the next post.
Sarah Allen said…
so... how is the factory better than a plain old fixture?
jjinux said…
> so... how is the factory better than a plain old fixture?

1) The test data can be very near the test. 2) You can have instances inherit from each other. That way you only need to specify the things that are different and interesting. 3) You don't have a single fixtures file growing completely out of hand.
Rainer said…
>UserSession.create(users(:whomever)) # logs a user in

This didn't work for me. I used UserSession.create(:login => 'user', :password => 'password') like in the UserSessions Controller, for a normal login.
This set the current_user. Unfortunately I could use Factory Girl, because an association for the user which was created before login, did not exist after the user was logged in.
jjinux said…
As my tests are growing more and more complicated, I'm getting into situations where I don't know if I've already called Factory for a given user or not. Originally, I wrote code that would try to fetch the user from the database, and if that failed, it would call Factory. However, I found out that the object returned by Factory has a password, whereas the object returned by User.find_by_username! doesn't. Hence, I wrote the following function:

# Call "Factory user" and cache the results in the @users hash.
#
# user should be a symbol.
#
# This function serves two purposes. First of all, it prevents the same
# user from being passed to Factory twice, which would cause an error. Second
# of all, it returns the original factory object rather than looking up the
# record in the database. That's important because you can't get the password
# out of the database, and if you can't get the password out of the database,
# you can't use it to log into the site in your tests.
def user_factory(user)
user = user.to_sym # Just to be sure.
@users ||= {}
@users[user] ||= Factory user
en
jjinux said…
reset_session and Webrat don't play nicely with one another. See https://webrat.lighthouseapp.com/projects/10503/tickets/383-reset_session-and-webrat-dont-play-nicely-with-one-another for a workaround.

Popular posts from this blog

Ubuntu 20.04 on a 2015 15" MacBook Pro

I decided to give Ubuntu 20.04 a try on my 2015 15" MacBook Pro. I didn't actually install it; I just live booted from a USB thumb drive which was enough to try out everything I wanted. In summary, it's not perfect, and issues with my camera would prevent me from switching, but given the right hardware, I think it's a really viable option. The first thing I wanted to try was what would happen if I plugged in a non-HiDPI screen given that my laptop has a HiDPI screen. Without sub-pixel scaling, whatever scale rate I picked for one screen would apply to the other. However, once I turned on sub-pixel scaling, I was able to pick different scale rates for the internal and external displays. That looked ok. I tried plugging in and unplugging multiple times, and it didn't crash. I doubt it'd work with my Thunderbolt display at work, but it worked fine for my HDMI displays at home. I even plugged it into my TV, and it stuck to the 100% scaling I picked for the othe

ERNOS: Erlang Networked Operating System

I've been reading Dreaming in Code lately, and I really like it. If you're not a dreamer, you may safely skip the rest of this post ;) In Chapter 10, "Engineers and Artists", Alan Kay, John Backus, and Jaron Lanier really got me thinking. I've also been thinking a lot about Minix 3 , Erlang , and the original Lisp machine . The ideas are beginning to synthesize into something cohesive--more than just the sum of their parts. Now, I'm sure that many of these ideas have already been envisioned within Tunes.org , LLVM , Microsoft's Singularity project, or in some other place that I haven't managed to discover or fully read, but I'm going to blog them anyway. Rather than wax philosophical, let me just dump out some ideas: Start with Minix 3. It's a new microkernel, and it's meant for real use, unlike the original Minix. "This new OS is extremely small, with the part that runs in kernel mode under 4000 lines of executable code.&quo

Drawing Sierpinski's Triangle in Minecraft Using Python

In his keynote at PyCon, Eben Upton, the Executive Director of the Rasberry Pi Foundation, mentioned that not only has Minecraft been ported to the Rasberry Pi, but you can even control it with Python . Since four of my kids are avid Minecraft fans, I figured this might be a good time to teach them to program using Python. So I started yesterday with the goal of programming something cool for Minecraft and then showing it off at the San Francisco Python Meetup in the evening. The first problem that I faced was that I didn't have a Rasberry Pi. You can't hack Minecraft by just installing the Minecraft client. Speaking of which, I didn't have the Minecraft client installed either ;) My kids always play it on their Nexus 7s. I found an open source Minecraft server called Bukkit that "provides the means to extend the popular Minecraft multiplayer server." Then I found a plugin called RaspberryJuice that implements a subset of the Minecraft Pi modding API for B