【问题标题】:RSpec doesn't compare objects properlyRSpec 不能正确比较对象
【发布时间】:2014-01-24 19:37:47
【问题描述】:

我有以下规格:

require 'spec_helper'

describe Page do
  it "has a valid factory" do
    create(:page).should be_valid
  end

  it "is invalid without a title" do
    build(:page, title: nil).should_not be_valid
  end

  it "finds record" do
    page = create(:page, title: 'heyo')
    Page.unscoped.where(:title => 'heyo').should  == page
  end

  it "finds record with same attributes" do
    page = create(:page, title: 'heyo')
    Page.unscoped.where(:title => 'heyo').first.attributes.each do |name, val|
      expect(page[name]).to eq(val)
    end
  end
end

我有以下工厂:

model_statuses = ['published', 'draft']
FactoryGirl.define do
  factory :page do
    title         { Faker::Lorem.word + ' Page' }
    slug          { title ? title.parameterize : nil }
    intro         { Faker::Lorem.sentence 10 }
    content       { Faker::Lorem.sentences(5).join ' ' }
    status        { model_statuses.sample }
  end
end

测试失败:

Failures:

  1) Page finds record with same attributes
     Failure/Error: expect(page[name]).to eq(val)

       expected: Fri, 24 Jan 2014 23:33:47 MSK +04:00
            got: Fri, 24 Jan 2014 23:33:47 MSK +04:00

       (compared using ==)

       Diff:
     # ./spec/models/page_spec.rb:20:in `block (3 levels) in <top (required)>'
     # ./spec/models/page_spec.rb:19:in `each'
     # ./spec/models/page_spec.rb:19:in `block (2 levels) in <top (required)>'

  2) Page finds record
     Failure/Error: Page.unscoped.where(:title => 'heyo').should  == page
       expected: #<Page id: 1, slug: "heyo", title: "heyo", intro: "Sed sint et nesciunt earum libero eveniet est cupid...", content: "A sunt ab exercitationem quas ex incidunt numquam. ...", created_at: "2014-01-24 19:33:47", updated_at: "2014-01-24 19:33:47", status: "draft">
            got: [#<Page id: 1, slug: "heyo", title: "heyo", intro: "Sed sint et nesciunt earum libero eveniet est cupid...", content: "A sunt ab exercitationem quas ex incidunt numquam. ...", created_at: "2014-01-24 19:33:47", updated_at: "2014-01-24 19:33:47", status: "draft">] (using ==)

为什么这些对象及其属性不一样,如何正确检查对象是否相等?

【问题讨论】:

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


    【解决方案1】:

    我发现问题出在您的测试中比较不同的对象。

    以下测试:

      it "finds record" do
        page = create(:page, title: 'heyo')
        Page.unscoped.where(:title => 'heyo').should  == page
      end
    

    当您进行此测试时,您已经拥有来自 "has a valid factory" 测试的一个 page 对象。所以在这个测试执行之后你有两个页面对象,第一个是标题nil,第二个是标题heyo。现在,当您尝试与下一行中的should 匹配时,您的Page.unscoped.where(:title =&gt; 'heyo') 确实会返回预期结果,即标题为“heyo”的Page 对象数组。你得到一个数组并且你正在将数组与一个对象进行比较,所以这将失败。

    那么你的第二个测试"finds record with same attributes" 失败的原因是因为你期望第一个Page 对象等于最后一个Page 对象,所以这也将失败。

    因此修复将针对以下两个测试:

      it "finds record" do
        page = create(:page, title: 'heyo')
        Page.unscoped.where(:title => 'heyo').last.should  == page
      end
    
      it "finds record with same attributes" do
        page = create(:page, title: 'heyo')
        Page.unscoped.where(:title => 'heyo').last.attributes.each do |name, val|
          expect(page[name]).to eq(val)
        end
      end
    

    注意在两个测试中都使用了last

    database_cleaner gem 是您可能感兴趣的东西。它允许您在每次测试之前清理数据库。推荐看看。

    【讨论】:

    • 谢谢,我完全忘记了之前的存档。添加.last 使"finds record" 通过。但是"finds record with same attributes" 失败了,我检查了一下:(byebug) page.updated_at.to_f 1390598731.7451882 (byebug) page_saved.updated_at.to_f 1390598731.745188 所以存在时差。现在我很困惑。为什么一个测试通过而另一个失败?
    【解决方案2】:

    两个错误的来源是一样的。

    您正在比较一个日期,created_at,updated_at,它们的值似乎不同。

    请注意,当打印为字符串时,日期只会到第二个 - 它们可能在比这更低的级别上有所不同。

    要么从比较中排除日期,要么在故障现场的代码中进行提升/检查,以查看两项之间的小数秒是否不同。

    您可以使用的另一个技巧是在规范范围内将 Time.now/Time.new 存根,这样您将始终获得一致的结果。

    通常你会在你的规范上下文中的 before :each 块中存根/滥用 Time.now。您希望范围尽可能窄,因为如果您不小心在哪里执行,覆盖该方法可能会产生各种奇怪的副作用。

    【讨论】:

    • 谢谢。存根Time.now 的最佳方法是什么?在 RSpec 中通常如何比较对象?什么是最佳做法?
    • 如何在代码中提出/检查以查看实际值?
    • @leemour 当我说 raise/inspect 时,我的字面意思是在你失败的测试中调用 raise page.inspect,例如在你的“查找记录”示例中。
    • 这只会给我 1 个值。我怎样才能看到两个对象之间的差异?
    • 我检查并得到了结果(byebug) page.updated_at.to_f 1390598731.7451882 (byebug) page_saved.updated_at.to_f 1390598731.745188所以有一个时差。但是在我将.last 添加到Page.unscoped.where(:title =&gt; 'heyo') 之后,测试"finds record" 中的对象相等性是成功的。这是为什么呢?
    【解决方案3】:

    你必须在你的情况下冻结时间,例如使用timecop

    【讨论】:

      猜你喜欢
      • 2017-01-16
      • 2019-05-29
      • 1970-01-01
      • 1970-01-01
      • 2015-11-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-06-09
      相关资源
      最近更新 更多