简体   繁体   中英

Rails model validates and tests

I have a simple Rails 4 application, and I'm trying to write some tests to check my user signup is working properly. I have a problem that when I'm running testcases with my hands, everything works ok, but when I write some integration tests and run them it skips my model's validations, so I'm getting database exceptions like

Minitest::UnexpectedError: ActiveRecord::RecordNotUnique: PG::UniqueViolation: ERROR:  duplicate key value violates unique constraint "index_users_on_username"


test 'unique username violated' do
    params = user_params               # it is just a hash with User model attributes
    User.new(params).save              # saving user first time to violate unique after        
    params[:email] = 'Test@Email2'
    assert_no_difference 'User.count' do
        post users_path, user: params
    end
    assert_template 'users/new'
end

Example of my validation is below:

validates :username, :email, \
  uniqueness: {uniqueness: true, message: 'Username&email must be unique'}, \
  length: {maximum: 50, message: 'Length must be <= 50'}

It's not only this validation issue, but all the rest are not working in tests too.

If I'll validate my user object with something like, inside of a test, it will be false, as it should be.

User.new(params).valid?

My users#create action is below:

def create
    @user = User.new user_params
    if @user.save
        flash[:success] = 'Welcome to the Sample App!'
        redirect_to @user
    else
        render 'new'
    end
 end

my users#user_params method:

def user_params
    user_group = UserGroup.find_by_name 'administrator'
    if user_group.nil?
        user_group = UserGroup.new name: 'administrator'
        user_group.save
    end
    {username: 'TestUsername', password: 'TestPassword', \
password_confirmation: 'TestPassword', email: 'Test@Email', \
firstname: 'TestFirstname', lastname: 'TestLastname',user_group: user_group}
end

Rails validations don't really work that way. When you use:

validates :username, :email, uniqueness: true

You are adding a similar but separate validations for :username and :email . Consider this case:

user = User.new
user.valid?
assert(user.errors.has_key?(:username)) # pass
assert(user.errors.has_key?(:email)) # pass

When you print out the errors with user.errors.full_messages() it will contain both Email has already been taken and Username has already been taken .

But if you override the message like so:

validates :username, :email, uniqueness: { message: 'Username&email must be unique' }

You will get Username&email must be unique twice instead, which is not so great.

If for some reason you really need to have only one error you would add a custom validation method:

validate :uniqueness_of_username_and_email

def uniqueness_of_username_and_email
  if User.where("username = ? OR email = ?", username, email).any?
    errors.add(:email_username, 'Username&email must be unique')
  end
end

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM