【问题标题】:Rspec and FactoryGirl Uniqueness FailingRspec 和 FactoryGirl 唯一性失败
【发布时间】:2013-12-10 16:59:32
【问题描述】:

我将简要介绍代码示例,因为除了下面的测试之外,我的所有测试都通过了。我通过稍微改变一下让它通过了,但我不确定为什么第 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: 我意识到在我的工厂中我正在添加一个 initialize_with 块,如下所示:

initialize_with { Person.find_or_create_by(contact_number: contact_number) }

我意识到这可能是验证失败的原因——我只是让同一个人回来。但是,注释掉该行会出现以下错误:

Mongoid::Errors::Validations: 问题: 人员验证失败。 概括: 发现以下错误: 联系电话已被占用 解析度: 尝试使用有效数据保存文档或删除验证。

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

【问题讨论】:

  • 检查person2.should_not be_valid的错误信息。该记录可能不会因其他原因而无效(换句话说,它可能会因巧合而失败)。另外,比较第二个例子中的属性,看看数字是否真的相同。
  • 没有错误。那就是问题所在。应该有,但没有。同一个联系电话应该是无效的。
  • 记录上肯定有错误。如果person2.should_not be_valid通过,则说明person2.errors不能为空。
  • create(:person, contact_number: '111-111-11111') person2 = build(:person, contact_number: '111-111-1111') puts "Error are #{person2.errors. full_messages}" expect(person2).to have(1).error_on(:contact_number)....输出是 Error are [] expected 1 error on :contact_number, got 0

标签: ruby ruby-on-rails-4 factory-bot rspec-rails


【解决方案1】:

可能你的person factory在contact_number中有一个序列,在每个人中都有一个不同的contact_number。

只要意识到 build(:person) 没有验证。验证仅发生在 create 中。 我强烈建议使用shoulda-matchers 进行此类验证。

【讨论】:

  • 不,我在工厂中明确设置了contact_number。
  • 使用 create 仍然无法验证。我不同意“只需安装另一个 gem”作为答案。
【解决方案2】:

有可能您的数据库正在被清理(您的 Gemfile 中是否有 database-cleaner),或者您的测试没有按照您认为的顺序运行。 (在您的spec_helper.rb 中检查:random

虽然上述关于使用 shoulda-matchers 的答案将帮助您更简洁地在 RSpec 中运行此特定测试,但您可能希望您的唯一电话号码测试能够完全独立运行,而无需依赖已执行的另一个规范.您的第二个测试是 Obscure Test 的一个示例(还有一点 Mystery Guest http://robots.thoughtbot.com/mystery-guest),从测试代码中不清楚实际测试的是什么。您的电话号码参数是在另一个文件(工厂)中定义的,并且之前的数据设置正在文件中其他地方的另一个规范中运行。

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

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

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

【讨论】:

  • 实际上,我在您回复之前尝试过同样的事情,但我复制/粘贴了您在此处的内容以确保我没有疯掉。仍然失败。我确实安装了 Database Cleaner,但由于这两个对象都是在同一个测试中创建的,所以数据库不会在其中被清理。该规范不依赖于任何其他规范,但我看到您关于工厂在其他地方定义的观点,尽管我认为如果我每次都必须为各种测试填写属性,这会违背目的。
  • 此外,测试是随机运行的,但同样,由于两个工厂都是按照相同的规范创建/构建的,因此顺序应该无关紧要。
【解决方案3】:

我想通了!一时兴起,我检查了测试数据库并注意到一个 Person 对象在周围徘徊。所以,其实并不是 build(:person).should_not be_valid 引发了 Mongoid 异常。之前是线上的create调用。清除数据库并再次运行规范通过了,但数据再次保持不变。我仔细检查了我的spec_helper.rb 文件,发现我没有在DatabaseCleaner 上调用start。我更新后的spec_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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多