Unit Testing – Should Private Methods Be Indirectly Tested?

unit testing

It seems to be fairly common opinion that you should only test public methods for a class or module and not private methods. If private methods are doing their job correctly, then that should be reflected in public methods.

That being said, when testing a public method that makes internal calls to private methods, should you test for things that could happen in that private method? Essentially, indirectly testing the private method?

Simplified example:

// Basic JavaScript module
let MyModule = function() {};
MyModule.prototype.taco = function() {
  ...
  burrito();
  ...
}

// Private function burrito
let burrito = function(){
  ...
  if (somethingHappened) {
    throw new Error('Oops!');
  }
  ...
}

In this case, the private method burrito is called from within the public taco method. In some situations, burrito can throw an error.

Should I write a test for taco where I am testing to see if an error ever gets thrown under certain situations? I'm not technically testing to see if the taco method itself throws an error, but rather if the underlying private method throws an error.

It's something I think is important to test, but since it's maybe bad practice to test private methods directly, is this the right way to test it? Or should I not test it at all? I just want to make sure that certain errors are thrown in a predictable manner.

For example, if burrito method makes an http request to some API server somewhere, but the server is offline at the time, it throws an error. It would be nice to write a test for this to ensure that when the server endpoint is offline, that the correct error is thrown.

Best Answer

Unit tests serve multiple purposes. In theory you should aim for 100% code coverage but in practice this doesn't always make sense (I.e not the best use of available resources). If you achieve 100% code coverage then by implication all code paths in all private methods have been executed, including your error case.

Another use for unit tests is to document all of the use cases envisaged by the original implementor. If you want to understand the capabilities and limitations of a class, reading the unit tests should tell you this much more effectively than reading the class itself. In this case if you envisage a use case where the consumer of your class should get an error response then you should document this use case by including a unit test for it.

It's not really about what is public vs what is private, the internal implementation of a class is allowed to change, and if the unit tests still pass then nothing should be broken by this refactoring. It is all about what is the expected behaviour of your class from the perspective of the rest of the system, and ensuring that this contract isn't broken by changes to the class.