1. Skip to content
  2. Skip to sidebar

Testing Twitter with OmniAuth

There are a number of things I really love using to develop web applications:

What happened to me, though, is combining all three created a perfect storm of frustration and annoyance. Never has so little ruby code caused me so much suffering! I will attempt to help you navigate the Twitter/OmniAuth/Cucumber testing storm and avoid all my woes!

Removing the frustration

What is extremely frustrating about testing authentication with the Twitter/OmniAuth combo is that it really is an extremely small amount of code and yet can potentially lead to hours of testing frustration. Once you know how to properly use OmniAuth in a mocked environment, it’s really just a 1-2-3 punch. The problem lies in that the full and proper solution doesn’t quite exist out on the internet right now. Part of this is due to Devise working in OmniAuth support and starting with OAuth2.0 (facebook) testing, the other part is that Twitter had the gall to force developers into using the https:// protocol very recently.

Step 1: Manually trigger OAuth callbacks

The first step is to make sure your Cucumber features / steps visit both the OmniAuth twitter provider page AND the callback page. In the context of your actual web application OmniAuth creates a route for “auth/:provider” that begins the OAuth authentication process and specifies a callback URL of “auth/:provider/callback”. In order for authentication to complete you need to head off to twitter and return back. Since you aren’t actually going to use Twitter, you need to manually kick off the callback URL.

Here are some sample step definitions for you:

#features/step_definitions/twitter_steps.rb
Given /^I am signed in$/ do
  visit twitter_auth_path
  # This is important!!!!! You must manually visit this page.
  visit twitter_callback_path
end  

When /^Twitter authorizes me$/ do
  visit twitter_callback_path
end

Step 2: Faking out Twitter

Your test suite should be immune to both Twitter outages and local internet connectivity issues. In order to accomplish this, we’re going to fake it’s responses with FakeWeb. I’ll just go ahead and reveal the whole shebang right here and now:

#features/support/fake_web.rb
require 'fake_web'

#I'm important for the secure get for verify_credentials
require 'net/https'

#During OAuthcalypse this base URL changed.
TWITTER_API_BASE = 'https://api.twitter.com/'

FakeWeb.allow_net_connect = false
FakeWeb.register_uri(:post, TWITTER_API_BASE + 'oauth/request_token',
  :body => 'oauth_token=fake'
)
FakeWeb.register_uri(:post, TWITTER_API_BASE + 'oauth/access_token',
  :body => 'oauth_token=fake&oauth_token_secret=fake'
)
FakeWeb.register_uri(:get,
  TWITTER_API_BASE + '1/account/verify_credentials.json',
  :body => File.join(Rails.root.to_s,
    'features', 'fixtures', 'verify_credentials.json'
  )
)

This is wee bit different than most of the other posts you’ll see out there. For one, this occurred after the great OAuthcalypse, and thus the API endpoint for Twitter is the new and shiny one. Second, I’m manually requiring Net::HTTPS. GET requests seem to have a significant amount of problems using https:// if you don’t manually include the library. The Net::HTTP library attempts to handle the request and bad things happen. You will see a fairly cryptic error message without manual inclusion of Net::HTTPS. Finally, you’re looking for the body of the verify_credentials request, not the response.

In order to get yourself some credentials in JSON, all you need to do is head here. Twitter is always looking out for you. Remember to obfuscate some of that information if you are checking this into a public repository and you are using your personal Twitter account!

Step 3: Not throwing your keyboard in a fit of rage

Now that you’ve gotten this far, let’s recap what potential issues we’ve avoided:

    • Our Cucumber steps manually visit the Twitter callback URL, forcing authorization.
    • We have faked the relevant portions of the Twitter API.
    • We have used the Twitter developers console to retrieve working account credentials
    • We have corrected any quirkiness of the new use of https:// over http:// in Twitter authentication.

Now you should hopefully have no need to destroy animate or inanimate objects. I hope that you instead convert that energy into writing some flipping awesome code!

3 Responses to "Testing Twitter with OmniAuth"

  1. Thanks for this article!
    Trying to implement it, I have some issues:

    I.) I try to set up my routes like this in order to get the twitter_callback_path

    match “/auth/:provider/callback” => “sessions#create”, :as => :twitter_callback

    but It raises the following error when I run cucumber:

    (::) failed steps (::)

    No route matches {:controller=>”sessions”, :action=>”create”} (ActionController::RoutingError)
    ./features/step_definitions/twitter_steps.rb:9:in `/^Twitter authorizes me$/’
    features/authentication.feature:9:in `And Twitter authorizes me’

    Here is my scenario:

    Feature: Authentication

    Scenario: Login via Twitter
    When I go to “the homepage”
    And I follow “Sign in with Twitter”
    And Twitter authorizes me
    Then I should see …

    II.) I would be pleased to know how I use the Twitter developers console. I enter my username as parameter and my password as value. I copy paste the request reponse in a verify_creditentials.json file and put it in the following folder: features/fixtures/

    the content is something like this:

    opening connection to api.local.twitter.com…
    opened
    <- "GET /1/account/verify_credentials.xml HTTP/1.1
    Accept: */*
    Connection: close
    User-Agent: OAuth gem v0.3.4.1
    Authorization: …
    Host: api.local.twitter.com:9000

    Is it the right way to do it?

    Thanks a lot for your help!

  2. Hi Ben,

    The first issue is due to the fact that the middle of the route is dynamic in nature and therefor Rails can’t actually use the :as condition to construct a named route. It even lies to you when you type in “rake routes” in the command line. There are two options:

    https://gist.github.com/826538

    On the second issue, all you need to do is click the “Response” button under the “Send” button you click. Then copy the response.json from the response.

    -Patrick Robertson

  3. Thanks Patrick!
    I’ll test that soon!!

Leave a Reply:

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

*

back to top