【问题标题】:Rails model validates and testsRails 模型验证和测试
【发布时间】:2015-12-02 13:29:24
【问题描述】:

我有一个简单的 Rails 4 应用程序,我正在尝试编写一些测试来检查我的用户注册是否正常工作。 我有一个问题,当我用手运行测试用例时,一切正常,但是当我编写一些集成测试并运行它们时,它会跳过我的模型的验证,所以我得到了像

这样的数据库异常
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

我的验证示例如下:

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

不仅是这个验证问题,其他的都不能在测试中工作。

如果我在测试中使用类似的东西来验证我的用户对象,那将是错误的,因为它应该是。

User.new(params).valid?

我的 users#create 操作如下:

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

我的用户#user_params 方法:

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

【问题讨论】:

  • 尝试唯一性:{message: '用户名和电子邮件必须是唯一的'}
  • 嗯,还是一样,没什么变化。
  • 显示您的 User#create 操作
  • 用 users#create body 更新了问题

标签: ruby-on-rails validation activerecord


【解决方案1】:

Rails 验证实际上并不是这样工作的。使用时:

validates :username, :email, uniqueness: true

您正在为 :username:email 添加类似但单独的验证。考虑这种情况:

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

当您使用user.errors.full_messages() 打印出错误时,它将同时包含Email has already been takenUsername has already been taken

但是如果你像这样覆盖消息:

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

你会得到两次Username&amp;email must be unique,这不是很好。

如果由于某种原因你真的只需要一个错误,你会添加一个自定义验证方法:

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

【讨论】:

  • 谢谢,我注意到它会为每个字段生成单独的验证,但无论如何它都不起作用。问题不在于它的工作方式,而是它确实可以工作(!)如果作为应用程序运行并且我正在检查我的视图,但如果我通过测试检查相同的事情,它将无法工作
  • 会不会是重复值是null?典型的 postgres 唯一性索引不允许两个 null 值,但 validates uniqueness 允许一个 nil 值。
  • 如果您真的在测试中写出了user_params 的值,因为它是拼图的关键部分,可能会更容易排除故障。
  • 还从您的控制器中添加 user_params 方法 - 如果您忘记将用户名参数列入白名单,它将为 nil,这将导致 PG::UniqueViolation,因为它没有被模型验证“捕获” .
  • 添加了我的 users_params 方法,希望有助于理解问题所在
猜你喜欢
  • 2015-10-09
  • 2011-11-24
  • 1970-01-01
  • 2017-05-28
  • 1970-01-01
  • 2023-03-07
  • 2016-02-24
  • 2012-12-25
  • 1970-01-01
相关资源
最近更新 更多