How to spec a private method

Solution 1:

I always take this approach: I want to test the public API my class exposes.

If you have private methods, you only call them from the public methods you expose to other classes. Hence, if you test that those public methods work as expected under all conditions, you have also proven that the private methods they use work as well.

I'll admit that I've come across some especially complex private methods. In that extreme case you want to test them, you can do this:

@obj.send(:private_method)

Solution 2:

For the private methods that need code coverage (either temporarily or permanently), use the rspec-context-private gem to temporarily make private methods public within a context.

gem 'rspec-context-private'

It works by adding a shared context to your project.

RSpec.shared_context 'private', private: true do

  before :all do
    described_class.class_eval do
      @original_private_instance_methods = private_instance_methods
      public *@original_private_instance_methods
    end
  end

  after :all do
    described_class.class_eval do
      private *@original_private_instance_methods
    end
  end

end

Then, if you pass :private as metadata to a describe block, the private methods will be public within that context.

class Example
  private def foo
    'bar'
  end
end

describe Example, :private do
  it 'can test private methods' do
    expect(subject.foo).not eq 'bar'
  end
end