RSpec: Stub private method
Asked Answered
C

4

37

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?

Crat answered 20/2, 2013 at 18:21 Comment(0)
L
41

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

Latakia answered 20/2, 2013 at 18:24 Comment(4)
eehmm... I'm sure that it works. Can you explain it more what you mean?Crat
I tried it out, when I try to call should_receive with a private method, I get private method start_training' called for #<Run:0x007f7f7e2ebe68>`Crat
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...Latakia
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!Crat
D
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

Diptych answered 26/7, 2016 at 10:25 Comment(1)
using allow_any_instance_of is considered a bad practise, see rubydoc.info/gems/rubocop-rspec/1.6.0/RuboCop/Cop/RSpec/…Crat
V
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.

Vachill answered 20/2, 2013 at 18:23 Comment(3)
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.Summarize
@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.Sparerib
They didn't say they wanted to test private methods. But the test relied on some private methods that he wanted to stub out.Fanatic
P
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")
)
Pamilapammi answered 9/10, 2019 at 8:13 Comment(3)
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 rubydoc.info/gems/rubocop-rspec/1.6.0/RuboCop/Cop/RSpec/…)Crat
Why is it considered a smell @23tux?Laterality
@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.Lepsy

© 2022 - 2024 — McMap. All rights reserved.