Ruby Rspec和FactoryGirl唯一性失败

Ruby Rspec和FactoryGirl唯一性失败,ruby,ruby-on-rails-4,factory-bot,rspec-rails,Ruby,Ruby On Rails 4,Factory Bot,Rspec Rails,我将简要介绍代码示例,因为除了下面的测试之外,我所有的测试都通过了。我通过稍加修改使之通过,但我不确定为什么版本1失败,版本2工作 我的模型: # app/models/person.rb class Person validates :contact_number, uniqueness: true end 模型规格 # spec/models/person_spec.rb require 'spec_helper' describe Person do it 'is a valid f

我将简要介绍代码示例,因为除了下面的测试之外,我所有的测试都通过了。我通过稍加修改使之通过,但我不确定为什么版本1失败,版本2工作

我的模型:

# app/models/person.rb
class Person
validates :contact_number, uniqueness: true
end
模型规格

# spec/models/person_spec.rb
require 'spec_helper'
describe Person do
  it 'is a valid factory' do
    create(:person).should be_valid # passes
  end

  it 'has a unique phone number' do
    create(:person)
    build(:person).should_not be_valid # fails
  end

  it 'also has a unique phone number' do
    person1 = create(:person)
    person2 = person1.dup
    person2.should_not be_valid # passes
  end
end
据我所知,两个唯一性测试应该做相同的事情,但是一个通过,一个失败

如果有关系的话,我正在使用mongoid,尽管我认为这不会有任何效果。我也没有在测试中使用嵌套上下文或描述做任何事情,所以我认为范围是正确的。任何见解都值得赞赏

更新1:我在我的工厂里意识到,我正在添加一个
初始化\u,块如下所示:

initialize_with { Person.find_or_create_by(contact_number: contact_number) }
我意识到这可能是验证失败的原因——我只是让同一个人回来。但是,注释掉该行会产生以下错误:

Mongoid::错误::验证: 问题: 人员验证失败。 总结: 发现以下错误:联系人号码已被占用 决议: 尝试使用有效数据持久化文档或删除验证


从理论上讲,这是很好的,我想,因为它不会让我用相同的联系号码保存第二个人,但我更希望我的测试通过。

可能你的个人工厂在联系号码中有一个序列,每个人都有一个不同的联系号码

只需意识到构建(:person)不会验证。验证仅在创建中进行。
我强烈建议使用进行此类验证。

可能是您的数据库正在被清理(您的GEM文件中是否有数据库清理器),或者您的测试没有按照您认为的顺序运行。(在您的
spec\u helper.rb
中检查
:random

虽然上面关于使用
shoulda matchers
的回答将帮助您更简洁地在RSpec中运行此特定测试,但您可能希望您的唯一电话号码测试能够完全独立运行,而无需依赖已执行的其他规范。您的第二个测试是一个晦涩难懂的测试(也是一个有点神秘的客户),从测试代码中不清楚实际测试的是什么。您的电话号码参数在另一个文件(工厂)中定义,先前的数据设置正在该文件其他地方的另一个规范中运行

您的第二个测试已经更好了,因为它更明确地显示了您正在测试的内容,并且不依赖于已经运行的另一个规范。我会这样写,让它更明确:

it 'has a unique phone number' do
  person1 = create(:person, phone_number: '555-123-4567')
  person2 = create(:person, phone_number: '555-123-4567')

  # can use 'should' here instead
  expect(person2).not_to be_valid
end

如果您没有明确说明电话号码,那么如果您更改了工厂,即使您的代码仍然正确,此测试也可能开始失败。此外,如果您有其他要验证其唯一性的属性,则即使缺少电话号码验证,您以前的测试也可能通过。

我找到了!一时兴起,我查看了测试数据库,发现一个Person对象在周围徘徊。所以,它实际上不是
build(:person)。不应该是引发Mongoid异常的有效的
。这是之前在电话线上的create呼叫。清除数据库并再次运行规范通过了,但数据仍然保持不变。我仔细检查了我的
spec\u helper.rb
文件,发现我没有在
DatabaseCleaner
上调用
start
。我更新的
spec\u helper.rb
文件如下所示,现在一切正常:

# Clean up the database
require 'database_cleaner'

config.mock_with :rspec

config.before(:suite) do
  DatabaseCleaner.strategy = :truncation
  DatabaseCleaner.orm = "mongoid"
end

config.before(:each) do
  DatabaseCleaner.start
end

config.after(:each) do
  DatabaseCleaner.clean
end

否,我正在工厂中显式设置联系人号码。使用“创建”仍然无法验证。我不同意“只需安装另一个gem”作为答案。事实上,在你回复之前,我也尝试过同样的方法,但我复制/粘贴了你在这里的内容,以确保我没有发疯。仍然失败。我确实安装了数据库清洁器,但是由于这两个对象都是在同一个测试中创建的,所以DB不会在中间被清理。此规范不依赖于任何其他规范,但我明白您关于工厂定义的观点,尽管我认为如果每次都必须为各种测试填写属性,则会达不到目的。此外,测试是随机运行的,但同样,因为两个工厂都是在同一规范中创建/构建的,顺序应该无关紧要。检查
人员的错误消息2。错误消息应该无效。该记录可能不会因其他原因而无效(换句话说,它可能因巧合而失败)。另外,比较第二个例子中的属性,看看数字是否相同。没有错误。这就是问题所在。应该有,但没有。与同一联系人号码不应有效。记录上肯定有错误。如果
person2.should\u无效
passs,则表示
person2.errors
不能为空。create(:person,contact\u number:'111-111-11111')person2=build(:person,contact\u number:'111-111-1111')放入“Error is{person2.errors.full\u messages}”预期(person2)。有(1)。Error\u on(:contact\u number)..输出为错误[]应为1错误:联系电话号码,获得0