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

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

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

Creating Windows 10 Boot Media for a Lenovo Thinkpad T410 Using Only a Mac and a Linux Machine

TL;DR: Giovanni and I struggled trying to get Windows 10 installed on the Lenovo Thinkpad T410. We struggled a lot trying to create the installation media because we only had a Mac and a Linux machine to work with. Everytime we tried to boot the USB thumb drive, it just showed us a blinking cursor. At the end, we finally realized that Windows 10 wasn't supported on this laptop :-/ I've heard that it took Thomas Edison 100 tries to figure out the right material to use as a lightbulb filament. Well, I'm no Thomas Edison, but I thought it might be noteworthy to document our attempts at getting it to boot off a USB thumb drive: Download the ISO. Attempt 1: Use Etcher. Etcher says it doesn't work for Windows. Attempt 2: Use Boot Camp Assistant. It doesn't have that feature anymore. Attempt 3: Use Disk Utility on a Mac. Erase a USB thumb drive: Format: ExFAT Scheme: GUID Partition Map Mount the ISO. Copy everything from