【问题标题】:Rails Rspec test fails when it works in appRails Rspec测试在应用程序中工作时失败
【发布时间】:2017-10-10 19:32:13
【问题描述】:

我有以下代码和测试,我似乎无法通过。该代码应自动锁定所有已在 24 小时前完成的预订。

当我在测试中插入并运行 Booking.auto_lock_guests 的第一行时,没有任何反应。当我键入 booking_7 并在键入 Booking.auto_lock_guests 之后,它会更改为 true。这是否与 let 的设置方式有关,它没有出现在 Booking.all 中?还是我写测试的方式?

任何帮助将不胜感激。

  def self.auto_lock_guests
    bookings = Booking.where(guests_completed: true, locked: false)
    bookings.each do |booking|
      next unless booking.guests_completed_at <= 1.day.ago
      booking.locked = true
      booking.save
    end
  end


  context 'auto_lock_guests' do
    let(:booking_6) { FactoryGirl.create(:booking, date: Date.today - 5.day, guests_completed: true, guests_completed_at: DateTime.now, locked: false )}
    let(:booking_7) { FactoryGirl.create(:booking, date: Date.today - 5.day, guests_completed: true, guests_completed_at: DateTime.now - 3.day, locked: false )}
    before do
      Booking.auto_lock_guests
    end
    it 'should only lock bookings with a guests_completed date older than a day ago' do
      expect(booking_7.locked).to eq(true)
      expect(booking_6.locked).to eq(false)
    end
  end

【问题讨论】:

    标签: ruby-on-rails ruby rspec


    【解决方案1】:

    let 被懒惰地评估。当before 块被执行时没有记录,因为let 块还没有被调用。

    要么将let 更改为let! 以立即执行该块,要么在Booking.auto_lock_guests 之前调用booking_6booking_7

    编辑:

    你也不检查booking.save 是否成功。如果booking.save 失败 - 你永远不会知道。 :)

    next unless booking.guests_completed_at &lt;= 1.day.ago 可以重写为查询:where(Booking.arel_table[:guests_completed_at].gt(1.day.ago))

    【讨论】:

    • 这似乎没有解决它,但我现在已经解决了这个问题。感谢您提供有关检查保存的提示。我现在正在添加。
    【解决方案2】:

    您首先不需要遍历记录。事实上,当您的应用程序扩展时,它会导致问题,因为将所有这些记录拉入内存会耗尽服务器(或测功机)内存。

    您可以从数据库中选择记录并在单个查询中更新它们:

    class Booking
      def self.auto_lock_guests!
        bookings = Booking.where(guests_completed: true, locked: false)
                          .where('guests_completed_at <= ?', 1.day.ago)
        bookings.update_all(locked: true)
      end
    end
    

    许多单独的 UPDATE 查询和一次更新多行之间的执行时间差异可能很大。

    要对其进行测试,您可以创建多个记录并使用更改预期:

    # use describe and not context for methods.
    describe ".auto_lock_guests" do
      # let! is not lazy loading
      let!(:old_booking) { FactoryGirl.create(:booking, date: 7.days.ago, guests_completed: true, guests_completed_at: 3.days.ago, locked: false )}
      let!(:new_booking) { FactoryGirl.create(:booking, date: Date.today, guests_completed: true, guests_completed_at: DateTime.now, locked: false )}
    
       it 'locks a booking with a guests_completed date older than a day ago' do
         expect do
           Bookings.auto_lock_guests! && old_booking.reload
         end.to change { old_booking.locked }.from(false).to(true)
       end
    
       it 'does not lock a when guests_completed date is less than a day ago' do
         expect do
           Bookings.auto_lock_guests! && new_booking.reload
         end.to_not change { new_booking.locked }.from(false).to(true)
       end
    end
    

    在测试更改数据库的方法时使用change 是一个非常好的主意,因为它们会验证初始状态和结果。

    【讨论】:

    • 如果你真的必须从数据库中提取记录,请确保你这样做in batches
    • 还应将context 用于上下文,例如context "when the user is signed in"describe 块应该用来描述你正在测试的是什么。
    • 我正在处理我未在此处列出的预订,因为该部分有效。我肯定会改变它以拥有额外的地方,因为这将限制通过的预订数量。但我认为我无法执行 update_all,因为我似乎会丢失预订数组,然后在预订中找不到客人。
    • 这就是整个想法 - 记录没有加载到内存中。如果您必须将它们加载到内存中,请使用.find_each,但看看您是否可以通过使用连接查询来获取来宾来完成它,因为它的性能会更高。
    【解决方案3】:

    在调用 Booking.auto_lock_guests 之后,我最终不得不将其添加到之前的操作中,并且它起作用了。

    before do
      Booking.auto_lock_guests
      booking_7.reload
      booking_6.reload
    end
    

    【讨论】:

    • 我对这如何工作感到困惑,因为我确信在调用Booking.auto_lock_guests 时这两个预订都没有创建,但如果它有效,它就有效。跨度>
    猜你喜欢
    • 2023-03-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多