'Correct way to double a class instance with RSpec?

I'm trying to make a test double for a class instance with RSpec. Say I have a test that only accepts a File object as an argument.

Great, now how do I make a double so I don't have to pass in an actual file with all of my specs?

let(:file) { double(File) }
raise "NOT A FILE" unless file.is_a? File
# => RuntimeError: NOT A FILE

I've also tried this:

let(:file) { instance_double(File) }
raise "NOT A FILE" unless file.is_a? File
# => RuntimeError: NOT A FILE

And this (which is expecting an actual file):

let(:file) { object_double(File.new) }
# => ArgumentError: wrong number of arguments

What am I doing wrong?



Solution 1:[1]

You can just stub the is_a? call.

file = instance_double(File)
allow(file).to receive(:is_a?).with(File).and_return(true)

Solution 2:[2]

It is true you can stub #is_a?. But I would say, instead of trying to figure out what object it is, you could try to figure if it knows how to do the thing you need to do.

For instance, instead of received_file.is_a?(File), you could do received_file.respond_to?(:write).

This way, you can pass a Tempfile, or a File, or even an RSpec InstanceDouble(File). If the class knows how to #write then, we trust it.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 SteveTurczyn
Solution 2 Capripot