【问题标题】:Ordering by created_at in unit tests with generated data in rails在单元测试中按 created_at 排序,并在 rails 中生成数据
【发布时间】:2009-11-15 18:16:38
【问题描述】:

我有一些代码基本上显示了在给定表中所做的最后 x(变量,但假设 x 在这里是 20)更新。在其中一个单元测试中,我有这个 sn-p:

EditedItem.push_to_queue(hiddennow)
#create some new entries and save them
20.times{ EditedItem.push_to_queue(random_item) }
Queue.get_entries.each{|entry| assert_not_equal too_far_down, entry}

可能漂亮也可能不漂亮,但它传达了意图。 hiddennow 对象在队列中被推得太远,在调用 get_entries 时不应再返回。

#this works
SearchObject.find(:all, :order => "id desc")

#this does not, unless the 20.times loop has sleep(1) or something
SearchObject.find(:all, :order => "created_at desc")

这被简化了一点,但看起来 20.times 循环添加的东西足够快,以至于 created_at 上的 order by 子句无法区分。我的问题是,我在做一些根本错误的事情吗?如果不是,那么按照这些思路编写测试的更好方法是什么?

【问题讨论】:

    标签: mysql ruby-on-rails ruby unit-testing


    【解决方案1】:

    DigitalRoss 是对的。 created_at 的粒度为一秒。

    一个选项是在创建对象时设置created_at

    old = EditItem.new(:created_at => 1.second.ago)
    older = EditItem.new(:created_at => 2.seconds.ago)
    

    另一种选择是实际使用存根来处理Time 类。以下内容适用于 Rspec,但可以使用 Mocha 等其他模拟框架轻松完成。

    @seconds = Time.now.to_i
    Time.stub!(:now).and_return{Time.at(@seconds += 5) }
    

    这将在您每次调用Time.now 时返回一个比前一个多 5 秒的时间。

    如果你能做到的话,我会推荐第一种方法,因为它更清楚你在做什么,而且不太可能产生意想不到的后果。

    【讨论】:

    • 这是关键——我认为使用 Rails 学习和处理是一件棘手的事情。
    【解决方案2】:

    与文件和记录相关的时间(尤其是 Rails 中的那些时间)通常保存在 Unix timePOSIX 时间 中,这种格式保存秒数自 1970 年以来在算术类型中。

    因此,用于这些目的的时间具有 1 秒的粒度。

    Rails 无法订购 hiddennow 与随机项目之间没有至少一秒的延迟,并且根本不会订购 20 件套。

    【讨论】:

      【解决方案3】:

      rails 5 or 6 中的这些答案仍然正确吗?

      假设 User 模型上有一个旧的默认范围:

      #app/models/user.rb
      class User
        default_scope { order created_at: :desc }
      end
      

      以下rspec测试

        describe 'ordering in rails' do
          before(:each) do
            (0..9).each_with_index do |i|
              create :user, email: "#{i}@example.com"
            end
          end
          it 'preserves order' do
            puts User.pluck(:id, :created_at, :email)
            expect(User.all.pluck(:email).map(&:first)).to eq %w(9 8 7 6 5 4 3 2 1 0)
          end
        end
      

      产生以下输出:

      7602
      2020-01-07 09:33:14 UTC
      9@example.com
      7601
      2020-01-07 09:33:14 UTC
      8@example.com
      7600
      2020-01-07 09:33:14 UTC
      7@example.com
      7599
      2020-01-07 09:33:14 UTC
      6@example.com
      7598
      2020-01-07 09:33:14 UTC
      5@example.com
      7597
      2020-01-07 09:33:14 UTC
      4@example.com
      7596
      2020-01-07 09:33:14 UTC
      3@example.com
      7595
      2020-01-07 09:33:14 UTC
      2@example.com
      7594
      2020-01-07 09:33:14 UTC
      1@example.com
      7593
      2020-01-07 09:33:14 UTC
      0@example.com
      .
      
      Finished in 0.30216 seconds (files took 1.34 seconds to load)
      1 example, 0 failures
      

      因此,尽管所有模型都是在同一秒创建的,但顺序是一致的。查看这个rails 6 合并请求,看起来rails 5 似乎在主键上有一个隐式排序。我想知道 id 是否被用于在更高版本的 rails 中打破联系?

      【讨论】:

        猜你喜欢
        • 2012-01-19
        • 1970-01-01
        • 2016-11-26
        • 1970-01-01
        • 2013-06-11
        • 2023-03-10
        • 1970-01-01
        • 2017-03-04
        • 2012-05-22
        相关资源
        最近更新 更多