Unit testing private methods in Angular
Unit testing in Angular is challenging, especially when it comes to private method testing. There are times when it is so impossible that you don’t want to even test at all. In this article, I hope to provide some tips and tricks on how to do this more easily.
Disclaimer: This article only focuses on the “How” instead of the “Why” and the “Should/Should not” of unit testing private methods. Many people have raised concerns about the practice of testing private methods, but I am not here to debate that.
TLDR: Unit testing a private method can be done in a few ways:
- Testing through another public method.
- Converting the private method into a protected method, then create another class that extends and exposes this protected method in your unit test.
- Re-exposing the method as public but with “_” prefix to signify that it is a private method. (I do not recommend this unless your team agrees with this convention)
Inherently a private method is not accessible to the outside world, this is by design in the OOP world. However, in order to unit test just this method, one must be able to access it in an isolated fashion in order to perform the most isolated test possible — it seems like a contradiction between the concepts isn't it?
Get Duy Nguyen’s stories in your inbox
Join Medium for free to get updates from this writer.
With that being said, there are ways to get around this restriction to achieve what we want. The first is the most obvious one.
Testing through a public method
Supposed you have the following class that has a public init() method that takes in two parameters and calls a few methods in itself. This method also calls the private method setLanguage() that you want to test. To test our setLanguage() method, we would run the test on init() while mocking/stubbing away the part that we don’t want to test in init() itself.
As you can see, I’m using jasmine.createSpyObj() here quite heavily to “weed” out the parts I do not care about. Since the translationsMock variable is an empty array, only the call to setLanguage() really has an effect. Thus, the test is effectively only testing setLanguage()
Also a side note, I’m purposely not using Angular’s TestBed because it is simply not necessary. There are many articles written about how simply cutting out Testbed, when you don’t need to render anything on the browser for your test, will increase test performance. Personally, I also find it much easier to write and maintain as well.
This is a simple example where init() only call one other method, what if your method is much more complex and it is just too much effort to stub and mock everything? Well, there is another way.
Testing using an extended class
Since OOP provides us with another type of method, namely protected, we can leverage it to directly access the method that we want to test. This technique is great for maintaining encapsulation from public access but still allows us to run our unit test in an isolated manner.
Here, the private setLanguage is changed to protected, which enables us to create the I18nServiceExtended class in our spec file. This class exposes a method called setLanguageExtended(), which takes in the same parameters as the base setLanguage() method. It simply calls the setLanguage() without doing much else, thus allowing us to write tests “directly” against the base function — through calling i18nServiceExtended.setLanguageExtended(). Voila!
This technique has a really big advantage in that, it allows us to skip the extra stubbing and mocking for the init() method. In return, the tests are written specifically for the private method, and as a result, they are much simpler and direct tests, which improves maintainability and readability. Also, the extended class is not used anywhere else besides the spec file, so we can be sure that no one is suspiciously accessing our protected method.
While this is a good solution, there is still a small problem with it. It does not work if we don’t want to expose the setLanguage() method to the child classes. However, this is a really rare case that I seldom see in Angular apps. Mainly because Angular lifecycle components are not extended with traditional inheritance. Hence, inheritance only ever automatically works 100% with services, directives, and similar class-based objects. So there isn’t much value in using inheritance for components. This is good for us since we want to use it for testing a private method!
Using “_” prefix to mark a private method instead
The last technique is more or less a last resort kind of technique. I am personally not in favor of this technique unless your team is on board with it. Basically, instead of going through all the trouble of testing via another public method or creating a child class, we simply remove the private key and rename the method with an “_” (underscore) prefix. So in our example, private setTemplate() would simply become _setTemplate(). Then perform our test like we normally do with a public method.
This technique isn’t necessarily the worst thing in the world if your team follows and respects the pattern. This means that a method name starting with “_” should never be used outside of its class. Though this is achievable on an organization level, it definitely does not beat the compiler guard itself. When a new person is onboarded, they may not know/care enough to stop themselves from doing this. For that reason, I would advise against this. Of course, you may still do it if your team is disciplined enough.
There you have. Happy testing!
Support my work
I write articles for fun and make a small side income. I would appreciate any support for my work via a form of donation or simply following me to help me contribute to the community more in the future! Cheers =)







