0

I have a Rails 4.2.0 method that uses pessimistic locking to change a counter

class Foo < < ActiveRecord::Base
  def bump!
    transaction do
      lock!
      parent.lock!

      lock.counter += 1
      parent.counter += 1

      save!
      parent.save!
    end
  end
end

I am using Rspec 3.1 to test it like so

expect{foo.bump!}.to change(foo, :counter).by(1)
expect{foo.bump!}.to change(foo.parent, :counter).by(1)

The first change(foo, :counter) test passes but the second change(foo.parent, :counter) fails unless I comment out both lock! and parent.lock!

If I rewrite the failing test like this, it passes

prev_counter = foo.parent.counter
foo.bump!
expect(foo.parent.counter).to eq prev_counter + 1

Why doesn't it work with expect{...}.to change?

Chris Beck
  • 1,899
  • 2
  • 19
  • 26

1 Answers1

1

Your issue is that the instance of foo.parent in your RSpec test is not the same instance of parent that your Foo#bump! method is modifying, because calling parent.lock! reloads the parent association to get the lock and thus you modify a different instance than rspec has bound its own lambda to. The simplest fix is to use the change { } syntax which doesn't bind the receiver instance to foo.parent but instead just to foo, which doesn't change, like so:

expect{foo.bump!}.to change{foo.counter}.by(1)
expect{foo.bump!}.to change{foo.parent.counter}.by(1)

This fix worked locally for me.

Robert Nubel
  • 7,104
  • 1
  • 18
  • 30