Learning RSpec, Part II
Written by Jared Haworth on
UPDATED: April 22nd, 2010
I’ve been notified by one of my eagle-eyed readers that the solution originally posted at the end of this article is no longer correct. For whatever reason, RSpec no longer returns “Status” as one of the keys for the header.
The new, improved syntax is as follows:
it "should fail with invalid credentials" do
get :index
response.should_not be_success
response.status.should =~ /401/
end
Original Entry Follows
In the first entry, one of the issues I addressed was stubbing a response to an HTTP-Basic Authentication scheme. To get my spec to pass, I stubbed the entire authenticate method which I had written. While this approach was successful, it left me with a hole in my code coverage:

Now that I knew my controllers’ actions were being tested, I needed a good way to test the authenticate method itself, to ensure that failed requests were not being let through. A quick glance in the Rails source gave me some clues to implementation.
Since the credentials are sent (encoded) in the HTTP header, the first thing I needed to do was inject those credentials into the header before the request was processed. Rails core (using Test::Unit) uses the following convenience method in /actionpack/test/controller/http_authentication_test.rb
def set_headers(value = @credentials, name = 'HTTP_AUTHORIZATION')
@controller.request.env[name] = value
end
The encoded credentials are passed in and assigned to the request environment. Accomplishing this in RSpec wasn’t much different. First, we construct a set of encoded credentials, then we assign them to the environment variable ‘HTTP_AUTHORIZATION’. Note that in the RSpec code below, we don’t call @controller.request.env, but simply request.env
before(:each) do
@credentials = ActionController::HttpAuthentication::Basic.encode_credentials("david", "clearly_false")
request.env['HTTP_AUTHORIZATION'] = @credentials
end
This allowed me to create a series of tests to check that a user providing invalid credentials was not being allowed into the protected areas of the site.
it "should fail with invalid credentials" do
get :index
response.should_not be_success
response.headers["Status"].should =~ /401/
end
Two fairly simple expectations, when attempting to access a resource using invalid credentials, the response should not be a success, and the status code should match 401 (Unauthorized).
RSpec is definitely growing on me, and I’ve noticed some BDD disciplines starting to creep into my approach to testing with Test::Unit in my day job.