0

First of all I should probably mention that I'm very new to Rails and this is my first "serious" project, so I apologise if this is a simple question but I can't find an answer for it.

I'm using TDD in my project and am using RSpec to write the model tests, FactoryGirl to create the models and Faker to create dummy data for the models. Everything has been going really well until I added a test to make sure no two users have the same email address. In my User model I validated it like so:

# /app/models/user.rb
validates :email, :password_reset_code, :auth_token, uniqueness: true

My factory creates a user model with Faker, like so:

# /spec/factories/users.rb
FactoryGirl.define do
  factory :user do
    email { Faker::Internet.email }
    password { Faker::Internet.password }
    password_reset_code { Faker::Lorem.word }
    auth_token { Faker::Lorem.word }
  end
end

and my user_spec.rb test for this is as follows:

# /spec/models/user_spec.rb
it "is invalid with a duplicate email" do
  user = FactoryGirl.create(:user)
  FactoryGirl.create(:user, email: user.email).should_not be_valid
end

Here I'm creating a new model with FactoryGirl using its dummy values from Faker, saving it to the database and then creating another one with the same email as the first one. I'd expect RSpec to tell me this test passed because of the should_not be_valid part. But instead I get this output when I run the test:

Failures:

  1) User is invalid with a duplicate email
     Failure/Error: FactoryGirl.create(:user, email: user.email).should_not be_valid
     ActiveRecord::RecordInvalid:
       Validation failed: Email has already been taken
     # ./spec/models/user_spec.rb:19:in `block (2 levels) in <top (required)>'

So it seems that the model validation is raising an error which RSpec isn't catching and using to pass the test? I've managed to work around it by changing the test to this:

it "is invalid with a duplicate email" do
  begin
    user = FactoryGirl.create(:user)
    FactoryGirl.create(:user, email: user.email).should_not be_valid
  rescue
    false
  end
end

which seems to work, however I have a feeling this isn't the best way to do it.

What's the proper way to write this test?

John Dorean
  • 3,744
  • 9
  • 51
  • 82
  • im not sure if its relevant or not, but a quick search gave me this https://github.com/rspec/rspec-expectations/blob/master/Should.md – eno3nt Sep 29 '14 at 21:42

2 Answers2

1

I ran into this problem too. The error you're encountering is due to the fact that the create() method actually persists the model, which is then throwing an ActiveRecord::RecordInvalid at the DB layer (well, the persistence layer). If you want to assert if a model is valid or not you should use the build() method for your second object and then ask if it's valid. You can read up on it in this post.

Additionally, if you're just trying to test various validations on models and what not I wrote a quick and dirty gem that you can use to assert some of the more basic model validations. You can check it out here.

Hope that helps.

Community
  • 1
  • 1
mattforni
  • 855
  • 5
  • 11
  • FYI, if you were to use the gem I linked to you could test uniqueness via the following line of code: `Record.field_uniqueness(FactoryGirl.create(:user), :email)` – mattforni Sep 29 '14 at 22:12
  • Thanks, changed the second `create` to `build` and it's working great now. That gem looks useful, I'll take a look :) – John Dorean Sep 29 '14 at 22:55
0

I would go with:

# /spec/models/user_spec.rb
describe 'validations' do

  context 'with a duplicate email' do
    let(:other_user) { FactoryGirl.create(:user) }
    let(:attributes) { FactoryGirl.attributes_for(:user) }

    subject(:user)   { User.new(attributes.merge(email: user.email)) }

    it 'is not valid' do
      expect(user).to_not be_valid
    end
  end
end
spickermann
  • 100,941
  • 9
  • 101
  • 131