RSpec唯一性测试失败

时间:2018-04-14 16:42:49

标签: ruby-on-rails ruby rspec

我正在为用户帐户创建构建一些测试,并且遇到了我的一个测试问题。该测试应该验证用户名不能使用多次,并且由于某种原因测试失败,即使行为在实际应用程序本身中按预期工作。

奇怪的是,我有一个类似的测试设置来防止重复的电子邮件地址,并且该测试实际上正在按预期工作。由于它们的设置非常相似,我很惊讶一个正在工作,但另一个不是。

我发布了整个测试文件,以及下面的模型和迁移文件。失败的具体测试是第五次测试,“测试重复的用户名”。如果我可以提供任何其他文件,可能有助于解决此问题。

RSpec测试文件:

require 'rails_helper'

RSpec.describe User, type: :model do
  # initiate new users
  let(:user) {User.new}
  let(:user1) {User.new}

  # test new account without username
  it "is not valid without a username" do
    expect(user).not_to be_valid
  end

  # test username min length
  it "must have at least 5 chars in username" do
    user.username = 'a' * 4
    expect(user).not_to be_valid
  end

  # test username max length
  it "must have fewer than 25 chars in username" do
    user.username = 'a' * 26
    expect(user).not_to be_valid
  end

  # test proper username length
  it "must have between 5-25 chars in username" do
    user.username = 'a' * 6
    expect(user).to be_valid
  end

  # test duplicate usernames
    it "cannot use the same username multiple times" do
        user.username = 'aBCDEF'
        user1.username = 'aBCDEF'
        expect(user1).not_to be_valid
    end

  # test password min length
    it "must have at least 6 chars in password" do
        user.password = 'a' * 5
        expect(user).not_to be_valid
    end

    #test password max length
    it "must have no more than 25 chars in password" do
        user.password = 'a' * 26
        expect(user).not_to be_valid
  end

  # test multiple email addresses
    it "cannot use the same email multiple times" do
        user.email = 'dan@dan.com'
        user1.email = 'dan@dan.com'
        expect(user1).not_to be_valid
    end

end

用户模型:

class User < ApplicationRecord

  USERNAME_LENGTH = (5..25)
  PASSWORD_LENGTH = (6..25)

  validates_presence_of :username
  validates :username, length: USERNAME_LENGTH, uniqueness: true 
  validates :password, length: PASSWORD_LENGTH, allow_nil: true
  validates :email, uniqueness: true

  has_many :posts
  has_one :cart

  attr_reader :password

  def self.find_from_credentials(username, password)
    user = find_by(username: username)
    return nil unless user
    user if user.is_password?(password)
  end

  def is_password?(password_attempt)
    BCrypt::Password.new(password_digest).is_password?(password_attempt)
  end

  def password=(password)
    @password = password
    self.password_digest = BCrypt::Password.create(password)
  end
end

用户迁移文件:

class CreateUsers < ActiveRecord::Migration[5.2]
  def change
    create_table :users do |t|
      t.string :username
      t.string :session_token
      t.string :password_digest
      t.string :name
      t.string :email
      t.boolean :admin

      t.timestamps
    end

    add_index :users, :username
    add_index :users, :session_token

  end
end

1 个答案:

答案 0 :(得分:1)

您的测试失败,因为您已添加对用户名存在的验证,在该示例中,您不是要添加电子邮件,也不是useruser1

请参阅:

it "cannot use the same email multiple times" do
    user.email = 'dan@dan.com'
    user1.email = 'dan@dan.com'
    expect(user1).not_to be_valid
end

如果你在期望之前检查user1的有效性,你会看到

user1.valid?          # false
user1.errors.messages # {:username=>["can't be blank"]}

所以它不会因为唯一性验证而失败,但缺少用户名。

确保在测试前保存记录。在这两种情况下,如果存在具有相同属性的记录,Rails将检查数据库,并为您提供操作的布尔结果。

让我们转到rails控制台并检查持久化数据:

假设您有一个用户:

User.where(username: 'foo')
# [#<User:0x00007ff65cff14f0 id: 1, username: "foo", email: "foo@bar.com", ...]

现在尝试使用相同的用户名创建一个新用户:

bar = User.new(username: 'foo')
bar.valid? # false
# User Exists (0.2ms)  SELECT  1 AS one FROM "users" WHERE "users"."email" IS NULL LIMIT ?  [["LIMIT", 1]]
# User Exists (0.2ms)  SELECT  1 AS one FROM "users" WHERE "users"."username" = ? LIMIT ?  [["username", "foo"], ["LIMIT", 1]]
bar.errors.messages # {:username=>["has already been taken"]}

现在相同,但没有用户名为“foo”的记录:

User.where(username: 'foo')
# []
bar.valid? # true
User Exists (0.2ms)  SELECT  1 AS one FROM "users" WHERE "users"."email" IS NULL LIMIT ?  [["LIMIT", 1]]
User Exists (0.1ms)  SELECT  1 AS one FROM "users" WHERE "users"."username" = ? LIMIT ?  [["username", "foo"], ["LIMIT", 1]]

因此,您需要在每次检查唯一性时保存您的第一个用户,以便与其进行比较。

这可能有助于作为一个例子:

require 'rails_helper'

RSpec.describe User, type: :model do
  let(:user) { User.new(email: 'foo@bar.com', username: 'foo') }
  before { user.save }

  context 'username uniqueness' do
    let(:user1) { User.new(email: 'bar@foo.com', username: 'foo') }

    it { expect(user1).to_not be_valid }
  end

  context 'email uniqueness' do
    let(:user1) { User.new(email: 'foo@bar.com', username: 'bar') }

    it { expect(user1).to_not be_valid }
  end
end