Michael Bleigh’s TwitterAuth gem is truly full of awesome. It’s a complete OAuth authentication and API access solution for building Twitter apps with Rails. It uses familiar conventions borrowed from the Restful Authentication plugin, too. If you’re building a Rails-based app and you want to allow your users to Sign in with Twitter there’s just no better way to go.
For this particular app, I’m using the dynamic duo of Cucumber and Webrat to whip up integration tests. Since I initially stumbled a little bit when thinking about how to test integrated authentication against an external source like Twitter, I thought I’d doc the solution here in case other people were having the same issue.
Ready? Let’s do it.
First, install the TwitterAuth gem and use the provided generator to whip up the appropriate facilities. You’ll need to register your Twitter application accounts too. Or you know what? Screw it. If you want to make this super easy on yourself, Mike wrote a really great Twitter app Rails template that does all the setup for you, including walking you through getting the dev accounts. It’s nice, try it out. You’ll be up and writing Twitter apps in no time.
For the rest of this I’m going to assume that you have all of that working, and have installed Cucumber too. Don’t have Cucumber? Install it using RubyGems and then just run
script/generate cucumber inside your Rails app.
So let’s write a Cucumber feature to test authentication in our boilerplate Twitter template application. Put the following in
Feature: Authentication In order to create and edit games As a user I want to sign in with Twitter Scenario: Login via Twitter When I go to "the homepage" And I follow "Login via Twitter" And Twitter authorizes me Then I should see "Logged in as" Scenario: Checking login status Given I am signed in When I go to "the homepage" Then I should see "Logged in as" Scenario: Log out Given I am signed in When I go to "the homepage" And I follow "Log out" Then I should see "Login via Twitter"
Next, you’ll need to write step definitions to satisfy the missing steps. Do that by creating a file called
features/step_definitions/auth_steps.rb. The content of the file should define the following two steps:
Given /^I am signed in$/ do visit login_path visit oauth_callback_path end When /^Twitter authorizes me$/ do visit oauth_callback_path end
The secret sauce here is FakeWeb. We’ll use it to fake out responses from the Twitter auth service so that your integration tests stay local (and reliable). Make sure to
gem install fakeweb, and add the following to
config.gem "fakeweb", :version => ">= 1.2.5"
Now edit Cucumber’s
FakeWeb.allow_net_connect = false FakeWeb.register_uri(:post, 'http://twitter.com/oauth/request_token', :body => 'oauth_token=fake&oauth_token_secret=fake') FakeWeb.register_uri(:post, 'http://twitter.com/oauth/access_token', :body => 'oauth_token=fake&oauth_token_secret=fake') FakeWeb.register_uri(:get, 'http://twitter.com/account/verify_credentials.json', :response => File.join(RAILS_ROOT, 'features', 'fixtures', 'verify_credentials.json'))
Here we’re stubbing out the interaction with Twitter auth, and responding to all outbound authorization attempts with canned data. Note that this references a fixture file, containing a sample
verify_credentials API response from Twitter. You can obtain a copy using curl from the comfort of your terminal prompt (substitute your own username and password):
curl -i -u user:pass "http://twitter.com/account/verify_credentials.json" > verify_credentials.json
The last thing you’ll want to do is to check your
twitter_auth.yml file and make sure there’s a
cucumber environment defined in it. If not, you may need to add it.
And We’re Done
Alright that should do it. Go ahead and run
rake features. Everything should be green. And green is good. If you need to write other features that are dependent on a login requirement, you can reuse the same “Given I am signed in” step that we created earlier.
Thanks to b.kocik, whose original post on using FakeWeb to stub Twitter auth was 80% of the solution I needed here.