Read more

Ruby: Writing specs for (partially) memoized code

Dominik Schöler
November 15, 2016Software engineer at makandra GmbH

When you're writing specs for ActiveRecord models that use memoization Show archive.org snapshot , a simple #reload will not do:

it 'updates on changes' do
  subject.seat_counts = [5]
  subject.seat_total.should == 5
  # seat_total is either memoized itself, or using some
  # private memoized method
  
  subject.seat_counts = [5, 1]
  subject.seat_total.reload.should == 6 # => Still 5
end
Illustration online protection

Rails Long Term Support

Rails LTS provides security patches for old versions of Ruby on Rails (2.3, 3.2, 4.2 and 5.2)

  • Prevents you from data breaches and liability risks
  • Upgrade at your own pace
  • Works with modern Rubies
Read more Show archive.org snapshot

You might be tempted to manually unmemoize any memoized internal method to get #seat_total to update, but that has two disadvantages:

  1. You're doing something that never happens in reality. Your tests shouldn't do things your code never does.
  2. You're fiddling with internal state. When writing tests, stay on your chosen abstraction level.

Solution 1

record.unmemoize_all

Solution 2

Perform a full reload by re-finding your record:

it 'updates on changes' do
  subject.seat_counts = [5]
  subject.seat_total.should == 5
  
  subject.seat_counts = [5, 1]
  # Re-find subject to wipe memoization
  subject = described_class.find(subject.id)
  subject.seat_total.reload.should == 6 # Passes
end

Note that this does not work if you're testing unpersisted new records. In this case, there's probably something conceptually wrong and you either shouldn't expect to see updates or shouldn't memoize at all.

Posted by Dominik Schöler to makandra dev (2016-11-15 15:32)