rubyrspecrspec-mocks

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).


Solution

  • 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