Why is important to specify how many times given function is executed

Ruby on Rails / RSpec

RSpec allows you to expect how many times given method would be called. Before I will present how we can do this, is important to know why we want to do this. It’s all about having control over the behavior of our code. To better demonstrate it, let’s write invalid code:

class SomeClass
  def something
    @something = service.call
  end
end

Why is this code invalid? We want to use memoization here to not call service.call more than once each time SomeClass#something is called. However, instead of ||= we used = operator and our code does not work as expected.

If in our code we would write something like this:

expect(some_class_instance).to have_received(:something)

We would not catch our mistake. Our example is really simple but imagine that service.call calls external API and consumes limited resources – then it’s really bad. How can we avoid it? By specifying how many times our method should be called:

expect(some_class_instance).to have_received(:something).once

And with such expectation, we would catch our mistake. If we would use Test Driven Development approach it would never be created. This case shows how is important to use TDD and write detailed expectations.

RSpec syntax for expecting the message to be called more than once

What if we want to test if our code was called more than once or at least 2 or 3 times? RSpec allows us to specify this also.

Expect message to be called n times:

expect(some_class_instance).to have_received(:something).once
expect(some_class_instance).to have_received(:something).twice
expect(some_class_instance).to have_received(:something).exactly(3).times

Expect message to be called at least n times:

expect(some_class_instance).to have_received(:something).at_least(:once)
expect(some_class_instance).to have_received(:something).at_least(:twice)
expect(some_class_instance).to have_received(:something).at_last(3).times

Expect message to be called at most n times:

expect(some_class_instance).to have_received(:something).at_most(:once)
expect(some_class_instance).to have_received(:something).at_most(:twice)
expect(some_class_instance).to have_received(:something).at_most(3).times