August 28, 2013

Testing Monban with factory_girl

Update! Feel free to read this article, but then check the better way how to handle this.

I was working on authentication system for my recent Rails project. I've decided to use monban gem for it.

I've started with simple integration test for user sign in.

require 'spec_helper'

feature 'Sign in as user' do
  scenario 'with valid login and password' do
    FactoryGirl.create(:user, username: 'user', password: 'password') 
    visit root_path
    fill_in 'Meno', with: 'user'
    fill_in 'Heslo', with: 'password'
    click_button 'Prihlásiť'

    expect(current_path).to eq albums_path
    expect(page).to have_css '.alert-notice', text: 'Prihlásenie bolo úspešné.'
    expect(page).to have_link 'Odhlásiť', href: session_path
  end
end

And it failed immediately on factory_girl. So I've created factory file.

FactoryGirl.define do
  factory :user do
    username 'user'
    password 'password'
  end
end

I've set up monban and users table.

rails g monban:scaffold
rake db:migrate

Created migration to remove email column and add username column.

rails g migration AddUsernameToUsers username
vi db/migrate/...
rake db:migrate
class AddUsernameToUsers < ActiveRecord::Migration
  def change
    remove_column :users, :email
    add_column :users, :username, :string
  end
end

It was easy until now. But then I started to struggle with monban. There is no method for setting password in User model. I've spent some time figuring how to overcome it (note to myself: don't code during the night ;). I was able to fix it just other day, but solution was surprisingly easy. I've created password= method ;).

class User < ActiveRecord::Base
  def password=(password)
    self.password_digest = Monban.encrypt_token(password)
  end
end

And it was working!

But I've forgot one thing. To write the test first! Hmm, but I did not know how to test it before, so I've wrote it afterwards.

require 'spec_helper'

describe User do
  it 'responds to username' do
    expect(User.new).to respond_to :username
  end

  describe '#password=' do
    it 'sets password_digest to Monban password hash' do
      user = User.new
      user.password = 'password'
      expect(Monban.compare_token(user.password_digest, 'password')).to be_true
    end
  end
end

Then I've just followed errors from integration test and finally user sign in was done!

p.s. When I am thinking about it now, probably last unit test is useless, because I do no plan to use password= anywhere in application. Anyway it is better to have additional test than no test at all.

Hey there!

My name is Patrik Bóna and I am the only programmer at Memberful. This blog is kind of dead, but I just started my own Ruby on Rails screencast. Follow me on Twitter if you want to be notified about my newest videos.