2

After reading this question I really do not like the answer.

Rails / RSpec: How to test #initialize method?

Maybe I am having a third scenario. This is what I have now, inspired by second code from that answer.

# Picture is collection of SinglePictures with same name and filename,
# but different dimensions
class Picture
  attr_accessor :name, :filename
  attr_reader :single_pics, :largest_width

  def initialize(name, filename, dimensions=nil)
    @largest_width = 0
    @single_pics = {}
    add_single_pics(dimensions) if dimensions
  end

  def add_single_pics(max_dimension)
    # logic
  end
end

describe '#initialize' do
  it 'should not call add_single_pics if dimensions is not given' do
    subject = Picture.new('Test Picture', 'Test-Picture')
    expect(subject.largest_width).to eq 0
  end

  it 'should call add_single_pics if dimensions are given' do
    subject = Picture.new('Test Picture', 'Test-Picture', 1920)
    expect(subject.largest_width).to eq 1920
  end
end

I really don't like this because I am testing the functionality of add_single_pics in #initialize tests. I would like to write somehow this in spec:

  expect(subject).not_to have_received(:add_single_pics)
  expect(subject).to have_received(:add_single_pics)

But I get

Expected to have received add_single_pics, but that object is not a spy
or method has not been stubbed.

Can I fix this somehow?

Community
  • 1
  • 1
Marko Avlijaš
  • 1,579
  • 13
  • 27

1 Answers1

4

Spies are an alternate type of test double that support this pattern by allowing you to expect that a message has been received after the fact, using have_received.

https://relishapp.com/rspec/rspec-mocks/v/3-5/docs/basics/spies

Only spy object can store the method calls. To test your real class in the way that you want, you have to use expect_any_instance_of statement before the class will be initialized:

expect_any_instance_of(Picture).to receive(:add_single_pics)
Picture.new('Test Picture', 'Test-Picture')

In this case your add_single_pics method will be called, but its logic will not be run, if you need to run it you need to call the and_call_original method on the matcher:

expect_any_instance_of(Picture).to receive(:add_single_pics).and_call_original
ole
  • 5,166
  • 5
  • 29
  • 57
  • Too bad they say it's for legacy code and wanting to use it is a code smell. Is this code I wrote smelly? LOL. I wanted to save a line of code when USING `Picture` class, not having to call add_single_pics after new. – Marko Avlijaš Oct 15 '16 at 08:32