我正在为用户帐户创建构建一些测试,并且遇到了我的一个测试问题。该测试应该验证用户名不能使用多次,并且由于某种原因测试失败,即使行为在实际应用程序本身中按预期工作。
奇怪的是,我有一个类似的测试设置来防止重复的电子邮件地址,并且该测试实际上正在按预期工作。由于它们的设置非常相似,我很惊讶一个正在工作,但另一个不是。
我发布了整个测试文件,以及下面的模型和迁移文件。失败的具体测试是第五次测试,“测试重复的用户名”。如果我可以提供任何其他文件,可能有助于解决此问题。
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
答案 0 :(得分:1)
您的测试失败,因为您已添加对用户名存在的验证,在该示例中,您不是要添加电子邮件,也不是user
或user1
。
请参阅:
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