Back

Cucumber testing with OmniAuth

Yesterday I needed to write some cucumber tests to cover the login and new user creation aspects of a RoR site I am working on. The site handles authentication via OAuth using the incredibly handy OmniAuth Gem, which I highly recommend. At first I thought I would have to decide to either bypass, and therefore not really test, much of the OAuth functionality, or I would need to spend a log of time mocking out the interaction with the external OAuth providers. However, I quickly learned from this blog post that Omniauth added some really awesome testing functionality which does this for you.

I wanted to test that existing users could login using the various OAuth providers. My cucumber test case ended up looking like the following:

@omniauth_test
Scenario Outline: User Login
  Given A user with name "Inigo Montoya" and UID "42" and auth provider "<provider>"
  When I visit the home page
  And I login using "<provider>" as the user
  Then I should see "My Profile" in the header
  And I should see "Sign Out" in the header
  And I should be on the "home" page

  Examples:
    |provider|
    |google |
    |twitter |
    |facebook|

In the first step I need to create a user that included an auth provider, which proved slightly trickier than I expected. My Application supports multiple auth providers and my user and auth_provider models look like the following:

class User < ActiveRecord::Base

  has_many :auth_providers

  def self.create_with_omniauth(auth)
    create! do |user|
      user.auth_providers << AuthProvider.new(:name => auth['provider'], :uid => auth['uid'])
      name = auth['info']['name']
      user.name = name
    end
  end
end

class AuthProvider < ActiveRecord::Base
  belongs_to :user
end

I am using FactoryGirl and my factory definitions look like this:

factory :user do
  ignore do
    uid '1234'
    provider 'google_oauth2'
  end

  after(:create) do |user, evaluator|
    FactoryGirl.create :auth_provider, :user => user, :uid => evaluator.uid, :name => evaluator.provider
  end
end

factory :auth_provider do
end

Which allows me to write a user creation step like this:

Given /^A user with name "(.*?)" and UID "(.*?)" and auth provider "(.*?)"$/ do |username, uid, provider|
  provider.downcase!
  provider = 'google_oauth2' if provider.eql?('google')
  @user = FactoryGirl.create :user, :name => username, :uid => uid, :provider => provider
end

Then, after visiting my home page, I have my login step which looks like this.

When /^I login using "(.*?)" as the user$/ do |provider|
  logon(provider, @user.name, @user.auth_providers.first.uid)
end

def logon(provider, username='Inigo Montoya', oauth_uid='123')
  provider.downcase!
  provider = 'google_oauth2' if provider.eql?('google')
  OmniAuth.config.add_mock(provider.to_sym, {:uid => oauth_uid, :provider => provider, :info => {:name => username}})

  visit '/'
  click_link 'Sign In'

  case provider
    when 'google', 'google_oauth2'
      click_link 'Google_128'
    when 'twitter'
      click_link 'Twitter_128'
    when 'facebook'
      click_link 'Facebook_128'
    else
      fail("Unknown auth provider: '#{provider}'")
  end
end

If there is any magic, it’s in the line with the call to Omniauth.config.add_mock which basically sets up a callback with the supplied data. However for that to work, you have to put OmniAuth in test mode, which is where the @omniauth_test tag in the original cucumber scenario comes in. I have that tag defined features/support/omniauth.rb which looks like this:

Before('@omniauth_test') do
  OmniAuth.config.test_mode = true
end

After('@omniauth_test') do
  OmniAuth.config.test_mode = false
end

I definitely have mixed feelings about using tags like this since it definitely would seem to be an example of technical details bleeding into scenarios which are supposed to be at a higher level of abstraction. However, it is a very convenient way to handle the overhead, so at least for now, I’ll handle it in this way.

3 responses to “Cucumber testing with OmniAuth”

  1. Awesome Post! (Actually this is just the author testing comments)

  2. Thanks for this post. This is exactly what I was looking for.

Leave a Reply

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