I have a service object something like:
class SecurityQueryService
class Contract::NotFound < StandardError; end
attr_reader :ticker_name, :contract_type, :contract
def initialize(user, params)
@user = user
@ticker_name = params[:ticker_name].upcase
@contract_type = params[:type]
end
def call
find_contract
end
private
def find_contract
@contract ||= contract_klass.find_by(ticker: ticker_name)
fail(
Contract::NotFound,
"Cannot find contract with ticker_name #{ticker_name}"
) if @contract.nil?
end
def contract_klass
return EquityContract if contract_type.nil?
"#{contract_type.strip.capitalize}Contract".constantize
end
end
And I have the following related spec:
require 'spec_helper'
describe SecurityQueryService do
let(:user) { create(:customer) }
let!(:equity_contract) { create(:equity_contract) }
describe "#call" do
describe "#find_contract" do
it "returns contract based on contract type." do
service = SecurityQueryService.new(user, {
type: 'equity',
ticker_name: equity_contract.ticker
})
service.call
expect(EquityContract).to receive(:find_by)
end
end
end
end
I want to make sure EquityContract
receives a find_by
message whenever I call #call
.
At the moment when I run the spec I get:
Failure/Error: expect(EquityContract).to receive(:find_by)
(EquityContract(id: integer, ticker: string, name: string, country: string, currency: string, instrument_type: string, bloomberg_ticker: string, benchmark_index: string, skip_valuation: boolean, isin: string, currency_factor: decimal, daily_update: boolean, id_bb_unique: string, image: string, domain: string) (class)).find_by(*(any args))
expected: 1 time with any arguments
received: 0 times with any arguments
How can I test this behaviour?
You need to set up the mock before you call the method. RSpec provides two ways to do this:
Move the expectation before service.call
:
describe "#find_contract" do
it "returns contract based on contract type." do
service = SecurityQueryService.new(user,
type: 'equity',
ticker_name: equity_contract.ticker
)
expect(EquityContract).to receive(:find_by)
service.call
end
end
Set up the method as a spy by allow
ing the method to be called, then expect it to have been called after the fact:
describe "#find_contract" do
it "returns contract based on contract type." do
service = SecurityQueryService.new(user,
type: 'equity',
ticker_name: equity_contract.ticker
)
allow(EquityContract).to receive(:find_by)
service.call
expect(EquityContract).to have_received(:find_by)
end
end
As you can see, the first method requires the least typing, but requires awkward thinking ahead. The second method is more verbose, but is more logical in that it puts the expectation after the method call.