hedgesky hedgesky - 7 months ago 55
Ruby Question

How to avoid using allow_any_instance_of?

Imagine we have following piece of code:

class A
def create_server
options = {
name: NameBuilder.new.build_name
}
do_some_operations(options)
end
end


To test such methods, I've used to use
allow_any_instance_of
:

it 'does operations' do
allow_any_instance_of(NameBuilder).to receive(:build_name)
# test body
end


But docs advise us not to use it because of several reasons. How then avoid
allow_any_instance_of
? I've came to only one solution:

class A
def create_server
options = {
name: builder.build_name
}
do_some_operations
end

private

def builder
NameBuilder.new
end
end


But with such approach code quickly becomes full of almost useless methods (especially when you actively using composition of different objects in described class).

Answer

If it is difficult to test, it means you have a problem in your class design. In your case, when you are doing testing for specific method call on a specific class within a class you are testing like this:

allow_any_instance_of(NameBuilder).to receive(:build_name)

Your test know exactly how the method is implemented internally. Your classes should encapsulate the logic and hide it. You are doing exactly the opposite.

You should not be testing any internal method logic. Just test the behaviour. Give inputs and test the correctness of the output.

If you really want to test that method call on NameBuilder class, then inject that dependency and make your class more testable. This also follows OOP principles.

class A
  def create_server(builder)
    do_some_operations(name: builder.build_name)
  end
end
Comments