I have the following code and test that I can't seem to make pass. The code should be auto locking all bookings that have been completed over 24 hours ago.
When I put a pry into the test and run the first line of Booking.auto_lock_guests nothing happens. When I type booking_7 and after type Booking.auto_lock_guests then it changes locked to true. Is this something to do with the way let is set up that it is not showing up in Booking.all? Or is it the way I have written the test?
Any help would be greatly appreciated.
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
let
is lazily evaluated. When the before
block is executed there are no records, because the let
blocks haven't yet been called.
Either change let
to let!
to execute the block immediately or call booking_6
and booking_7
right before Booking.auto_lock_guests
EDIT:
Also you don't check wether the booking.save
succeeded. If booking.save
failed - you would never know. :)
The next unless booking.guests_completed_at <= 1.day.ago
could probably be rewritten as a query: where(Booking.arel_table[:guests_completed_at].gt(1.day.ago))
You don't need to iterate through the records in the first place. In fact it will cause problems as your app scales since pulling all those records into memory will exhaust the servers (or dynos) memory.
You can select the records from the database and update them in a single query:
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
The difference in execution time between many individual UPDATE queries and an updating many rows at once can be massive.
To test it you can create multiple records and use change expectations:
# 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
Using change
is a very good idea when testing methods that change the database as they verify both the initial state and the result.
I ended up having to add this into the before action after calling Booking.auto_lock_guests and it worked.
before do
Booking.auto_lock_guests
booking_7.reload
booking_6.reload
end
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.