Rails 3 Tutorial Chapter 11 "Validation failed: Email has already been taken" error

Perry Horwich picture Perry Horwich · Mar 15, 2011 · Viewed 15.7k times · Source

My trouble arose in Chapter 11 of the Ruby on Rails Tutorial here.

I was seeing this rspec error:

Failure/Error: :user => Factory(:user, :email => Factory.next(:email)))
     ActiveRecord::RecordInvalid:
       Validation failed: Email has already been taken

first in user_spec.rb then in micropost_spec.rb. It was pretty puzzling. I thought the factory statements were generating a user in a fresh testing db each time autotest ran rspec. I checked out source files from the master branch with git and tried again, but saw the same error. I therefore suspected it related to the db contents somehow and not the code.

So, I did the following:

restarted "rails s"  
restarted autotest  
rake db:reset  
rake db:migrate  
rake db:test:prepare  
rake db:populate  

... and it all went green. The rspec tests passed.

There may be a more “to the point” solution, but I was thrilled this worked. Hope it helps someone else. I am left to conclude that my testing/development somehow added something to the db that was unexpected. I suppose the above steps are a good way to make yourself a fresh db near the end of chapter 11.

Was there a more direct way to solve this? Does the error indicate some other issue that I addressed without realizing it? I am left thinking that running rspec does not guarantee a fresh testing db each time. Is that a wrong assumption?

Answer

Carl Thuringer picture Carl Thuringer · Apr 24, 2011

I had trouble with the Integration tests shortly after Chapter 9.4. All of my controller tests and the request integration test blew up with the message 'Email has already been taken'

What I learned from RailsTutorial - chapter 8.4.3 - Test database not clearing after adding user in integration test is that you need to do something to clean up after integration tests, because unlike unit tests they may not clean up after themselves.

The solution presented there was to use the DatabaseCleaner gem, the implementation of which is also explained in the linked Question.

I think that if you don't implement some strategy for cleaning up after the integration test you will continue to have to use your 'shotgun' solution for cleaning up the DB every time you run the test suite. Definitely not fun.