Unit Testing Private Methods
| October 3, 2017 | in
Unit tests are great! Encapsulation is great! When writing testable code, sometimes you come across a situation where you want to test private methods. Before implementing any solution, please think about why you are wanting to test a private method and whether it should be tested.
If you still would like to test your private methods, here are a few options.
The obvious-but-never-recommended option is making your methods public. This breaks encapsulation and makes your service unusable (in my opinion). Imagine another developer wanting to call your service, but not knowing what method to call because everything is available.
Another solution would be to use InternalsVisibleTo. Inside your AssemblyInfo.cs you can add the following snippet with your Unit Testing project’s namespace and mark your private methods as internal. The InternalsVisibleTo attribute will treat your namespaced project as it was in the same assembly allowing you to invoke your methods. My two complaints about this solution are:
- The InternalsVisibleTo attribute is tucked away in AssemblyInfo.cs and for developers not familiar with this solution, can be hard to track down why another assembly is able to call internal methods on another assembly.
- I feel this is still breaking encapsulation because all we’ve done was narrowed down the encapsulation to the assembly.
The lesser-known solution I like to use is PrivateObject. This class is already included in the UnitTestFramework.dll so you don’t need to modify any other assemblies. To use this class, you pass the instance of the object you want to pass, then Invoke your private method by passing the string name of your method. My only complaint about this solution is having to pass the named string of your method. Lucky for unit tests, if you did happen to change your method name your unit test will fail throwing a System.MissingMethodException.
I’m sure there are other solutions out there, but I tend to favor PrivateObject more often than InternalsVisibleTo.
If there are other solutions out there to this problem, I would love to hear them!