Testing internal classes without making them public
…with a trick to make private methods testable too.
When doing automatic tests one often wants to get to the internal classes.
Testing internal stuff
The possibly simplest way is to use InternalsVisbleTo and write
1 | [assembly:InternalsVisibleTo("MyTestingProject.With.FullName)] |
in the testee’s AssemblyInfo.cs
Or one can write just before the namespace declaration too
1 2 3 4 | [assembly:InternalsVisibleTo("MyOtherProjectNameLikeExampleAbove")] namespace Whatever{ class Hello{ ... |
<shrugging shoulders>Thats all there is to it.</shrugging>
The namespace is System.Runtime.CompilerServices.
One can decorate InternalsVisibleTo with keys and stuff to harden who gets to the internals but why bother? As long as it is dotnet some reflection can get pass the threshold anyway.
Testing private stuff
Another way I have used is to make a wrapping class “UT_Customer” or method “UT_Create” that 1) wraps the internal class/method, 2) is public and 3) is clearly marked with something conspicuous like “UT?_”. If a drive by programmer sees a method with a weird name and doesn’t bother to read the xml comments and still uses the method and fails; I consider it his fault, not mine. Not that I would use an undocumented method, no never.
1 2 3 4 5 | private int AddCustomer(Customer customer){...} internal int UT_AddCustomer(Customer customer){ return AddCustomer(customer); } |
Moq caveat
There is a caveat where interfaces are not visible for Moq as described here.
The symptom is an error message like
Message: Initialization method Base.UnitTests.Command.CommandHandlerTest thre exception.
Castle.DynamicProxy.Generators.GeneratorException:
Castle.DynamicProxy.Generators.GeneratorException: Type Base.Command.IUndoRedoStack[[Base.Command.ICommand, Base, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]] is not public. Can not create proxy for types that are not accessible.
The remedy is to add
1 | [assembly:InternalsVisibleTo("DynamicProxyGenAssembly2")] |
Update
I see at Metzianou that one can add build variable stuff like $(AssemblyName) like so:
1 <InternalsVisibleTo Include="$(AssemblyName).Tests" />