Unit testing private methods using the internal keyword
Should you test private methods or should you refactor your code in someway to make the testing easier? And if yes, then how?
You should probably start by reading this excellent article Unit testing private methods that ask many valid questions if you should even try to do this kind of testing.
I will also try to answer them here.
TL;DR
By swapping out the private keyword for internal and granting the test projects the rights to access the internal methods you can test these methods.
You can also do the inverse or use dynamics.
The unit test
You have a class with some private methods that you whant/need to unit test but you don't know what to do?
There are few things you can do
- Move the private methods to another class and make the methods public/internal and unit test that class.
- Use reflection
- Change the method signature of private to internal and give the test assembly access to them by using InternalsVisibleTo.
- Do the reverse of InternalVisibleTo by using IgnoresAccessChecksTo. Here you would tell the unit test assembly to suppress the visibility checks of the assembly to test.
- Use PrivateObject class (that is a part of MSTest) to give the tests access to the private methods.
- Use dynamics
Lets take a closer look
1-2. Refactor/reflection
Out of scope for this article but I would probably reccomend looking at refactoring before anything else. And if that doesn't fit, look at the other steps.
3. InternalsVisibleToAttribute
If you just change your access modifiers from private to internal the only thing you need to do to make the test project be able to access that methods is to add the following line to some class (probably clearest to add it to AssemblyInfo.cs file).
[assembly: InternalsVisibleTo("MyAssembly.Tests")]
Now your MyAssembly.Tests project can access all the internal methods/classess in the project with that line added. All thanks to InternalsVisibleTo
Trouble shooting
"Strong-name signed assemblies must specify a public key in their InternalsVisibleTo declarations"
If you get the error above, the project you are adding this attribute to, is strongly signed.
You will need to add a .snk (strongly signed key without password) to your unit test project and then change
[assembly: InternalsVisibleTo("MyAssembly.Tests")]
to
[assembly: InternalsVisibleTo("MyAssembly.Tests, PublicKey=hex_value")]
Note: The hex_value needs to be in one big string line
You will have to build your test project and use the Developer Command Prompt to run the following action
sn -Tp MyAssembly.Tests.dll
4. IgnoresAccessChecksToAttribute
This is the reverse of the InternalsVisibleToAttribute
- can be used to test 3 party libraries
- code you don´t have access to or don't want to add new code to (e.g InternalsVisibleTo above)
But you need to jump over some hoops that are well described here.
The easiest way would be to access the source code here and then use it in your unit tests.
5. MSTest (V1) PrivateObject
Then we have the PrivateObject class that lets you invoke a private method on a class.
Here is a class with a private method that you might want to unittest.
And with PrivateObject that is easy.
I have a GitHub repo with both .net framework and .net Core projects to try it out.
I'll update that repository and this post when I get more information.
Note With MSTest2 .net Core the PrivateObject() seems to be missing and is probably not comming back.
But don't panic and take a look at nr. 4
6. Dynamic
Use Meziantou reccomendation and try out Meziantou.Framework
dynamic test = new ReflectionDynamicObject(new MyClass());
Assert.AreEquals(10, test._privateField);
Have a nice day and enjoy your testing :-)