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.
Awesome Post! (Actually this is just the author testing comments)
Thanks for this post. This is exactly what I was looking for.
You’re welcome, glad you found it helpful.