tob88
tob88

Reputation: 2211

How to test service object methods are called?

I'm trying to build some tests for my service objects.

My service file is as follows...

class ExampleService

  def initialize(location)
    @location = coordinates(location)
  end

  private

  def coordinates(location)
    Address.locate(location)
  end

end

I want to test that the private methods are called by the public methods. This is my code...

subject { ExampleService.new("London") }

it "receives location" do
  expect(subject).to receive(:coordinates)
  subject
end

But I get this error...

expected: 1 time with any arguments
received: 0 times with any arguments

Upvotes: 2

Views: 5921

Answers (2)

Alejandro Babio
Alejandro Babio

Reputation: 5229

How to test service object methods are called?

Short answer: Don't test at all

Long answer: After seen Sandi Metz advice on testing, you will be agree, and you will want to test the way she does.

This is the basic idea:

  • The public methods of your class (the public API), must be tested
  • The private methods don't need be tested

Summary of tests to do:

  • The incoming query methods, test the result
  • The incoming command methods, test the direct public side effects
  • The outgoing command methods, expect to send
  • Ignore: send to self, command to self, and queries to others

Taken from the slides of the conference.

Upvotes: 5

Peter Alfvin
Peter Alfvin

Reputation: 29389

In your first example, your subject has already been instantiated/initialized (by being passed to expect, invoking coordinates in the process) by the time you've set expectations on it, so there is no way for the expectation to receive :coordinates to succeed. Also, as an aside, subject is memoized, so there won't be an additional instantiation in the line that follows.

If you want to make sure your initialization calls a particular method, you could use the following:

describe do
  subject { FoursquareService.new("London") }
  it "receives coordinates" do
    expect_any_instance_of(FoursquareService).to receive(:coordinates)
    subject
  end
end

See also Rails / RSpec: How to test #initialize method?

Upvotes: 5

Related Questions