36

I try to test a class with RSpec2, that has some private methods, which are called from some public methods. I test the public methods with

@foo.should_receive(:start_training).exactly(2).times

if they are called and how often. My problem is, that this approach doesn't work with private methods. So, is there any way to use sth like @foo.send(:private_method) in combination with should_receive? Or any other syntax?

23tux
  • 14,104
  • 15
  • 88
  • 187

4 Answers4

37

should_receive(:method) works whether the visibility of :method is public or private.

Sebastián Palma
  • 32,692
  • 6
  • 40
  • 59
Justin Aiken
  • 745
  • 7
  • 10
  • 1
    eehmm... I'm sure that it works. Can you explain it more what you mean? – 23tux Feb 20 '13 at 18:32
  • I tried it out, when I try to call should_receive with a private method, I get `private method `start_training' called for #` – 23tux Feb 20 '13 at 19:14
  • Could you post a bit more code? In the spec I'm working on atm, I'm should_receive'ing private methods with no issues... – Justin Aiken Feb 20 '13 at 20:44
  • 1
    Silly me, I called the private method from another step of the test. So you were right, `should_receive` works in both cases, public AND private methods. Thanks! – 23tux Feb 21 '13 at 08:23
27

You can use allow_any_instance_of method to stub or mock any instance of a class for e.g. you have a class named Foo with some private methods than you can do something like this

allow_any_instance_of(Foo).to receive(:private_method) do
  #do something
end 

In case if there you have module also, you can do something like this

allow_any_instance_of(Module::Foo).to receive(:private_method) do
  #do something
end

You can find more details about allow_any_instance_of() method at Official Documentation

Sinscary
  • 652
  • 12
  • 32
  • 3
    using `allow_any_instance_of` is considered a bad practise, see https://www.rubydoc.info/gems/rubocop-rspec/1.6.0/RuboCop/Cop/RSpec/AnyInstance – 23tux Oct 16 '18 at 14:16
2

Why do you want to test the private methods? They're private for a reason; to prevent access from external calls. Testing the public methods that rely on the private methods should be sufficient.

Richard Brown
  • 11,346
  • 4
  • 32
  • 43
  • 8
    I'm pretty sure it worth it testing private methods. Otherwise, if you have 10 public methods that rely on that one private method, you could end up repeating yourself testing the behavior of all public those methods. – André Herculano Apr 09 '14 at 15:33
  • 5
    @AndréHerculano yes, but you should be testing behaviour, not implementation. You would test the returns of those public methods, regardless of what private methods they use. – Damien Roche Jun 16 '15 at 16:45
  • 8
    They didn't say they wanted to test private methods. But the test relied on some private methods that he wanted to stub out. – courtsimas Oct 01 '19 at 20:11
2

The bad news is: you can not stub private method.

The good one is: you can make your method protected and then stub it the usual way;

allow_any_instance_of(described_class).to(
  receive(:my_protected_method_name).and_return("foo_bar")
)
Sebastián Palma
  • 32,692
  • 6
  • 40
  • 59
Unkas
  • 3,522
  • 2
  • 19
  • 23
  • 1
    as pointed out in the answer above, you can stub private methods, and it has nothing to do with protected methods. Furthermore stubbing with `allow_any_instance_of` is considered a code smell and should be avoided (also recommended by Rubocop https://www.rubydoc.info/gems/rubocop-rspec/1.6.0/RuboCop/Cop/RSpec/AnyInstance) – 23tux Oct 10 '19 at 17:22
  • Why is it considered a smell @23tux? – Sebastián Palma Dec 31 '19 at 13:37
  • @SebastianPalma it's not. it's his opinion and the one of some rubocop fanatics. rubocop also say it's bad if your model in rails is > 250 lines (by default). some huge ass god models are easy 1000 and they are still slim. don't take rubocop for granted. – Tim Kretschmer Feb 07 '21 at 10:49