What's the Best Way to Unit Test Protected & Private Methods in Ruby

Should Private/Protected methods be under unit test?

No, I don't think of testing private or protected methods. The private and protected methods of a class aren't part of the public interface, so they don't expose public behavior. Generally these methods are created by refactorings you apply after you've made your test turn green.

So these private methods are tested implicitly by the tests that assert the behavior of your public interface.

On a more philosophical note, remember that you're testing behavior, not methods. So if you think of the set of things that the class under test can do, as long as you can test and assert that the class behaves as expected, whether there are private (and protected) methods that are used internally by the class to implement that behavior is irrelevant. Those methods are implementation details of the public behavior.

Should you cover private methods with a separate unit test

The idea is that your class exposes a an interface and your tests verify the input/output is correct from that interface. Each class/method should have a single responsibility.

The technique you described of marking a method as protected virtual is one that I have used myself on occasion, but this was within the context of a large legacy code application where it was impractical to refactor everything into a testable state right away. This technique should be used sparingly.

Generally if you feel that a private method is complex enough that it needs its own tests you should probably extract it out to its own class. If the private method does not have a lot of complexity then you will be needlessly asserting implementation details and making your code hard to refactor in future.

If you need to set up many mock objects to test a single class then it is probably violation SRP. One of the great side affects of writing tests is that it highlights any poorly designed classes.

I highly recommend this book to you: https://www.artofunittesting.com/

Should I test private methods using RSpec?

You can find an in-depth discussion of that very subject in these slides from a Sandi Metz talk.

https://speakerdeck.com/skmetz/magic-tricks-of-testing-railsconf

She says that you may test-drive your private methods if you like, but that the only test that you should worry about are the ones testing the public interface. Otherwise you may be coupling too tightly to implementation.

I think the point by toch, on splitting out service and value object and putting those under tests is also a good one if you are getting nervous about complex private methods that aren't tested.

How to test private functions in a module

I would say you have three options.

  1. Find a way to test the functionality using the class's public methods. All the class functionality should generally be exposed by its public methods. If you can't test a class just by using its public methods, this may indicate a deeper problem with the design.

  2. If option 1 doesn't work (and indeed sometimes it doesn't), refactor the private functionality out to a separate class, in which these methods are public. This seems to be what you're suggesting, and I don't see a problem with that. Testing is an integral part of the system you're building and should be treated as such. Making functionality public "just for testing" is completely valid in my opinion.

  3. Make the functions public and test them. This is technically easiest but is less clean than option 2.

PHPUnit testing a protected method that calls a private method which needs to be mocked

$result = $myProtectedMethod->invoke($reflectionClass, $mockArgOne, $mockArgTwo);

should be

$result = $myProtectedMethod->invoke($mmyMockClass, $mockArgOne, $mockArgTwo);

For more information how to use "invoke" method here. https://www.php.net/manual/en/reflectionmethod.invoke.php

Is unit testing private methods a good practice?

It's not a good practice (yet that doesn't mean you should never do that), and if possible you want to avoid it. Testing private method usually means your design could be better. Let's take a quick look at your player example:

  • moveToFilePos: sounds more like a responsibility of something doing I\O operations, not a music player's
  • fillBuffers: more of a memory manager's job rather than music player
  • checkIfValidTimeRange: again, probably could be moved out of player's scope to some simple validation class (seems like this one might be useful in other places aswell)

At the moment your music player does I/O, memory management and what not else. Is that all really in scope of its responsibilities?



Related Topics



Leave a reply



Submit