Yesterday I’ve worked on connector library used to access data warehouses provided by two external vendors. In order for it to be easy to use I limited public members to:
- library interface – can be used to aggregate connectors and testing
- two connector classes that provide public methods to retrieve prepared responses
- DTO request and response classes
In the project, there are several more interfaces and additional classes required to properly retrieve, process and present data. I didn’t want to obscure usability of the library, so I decided to mark all of them as internal. And obviously, there were problems with it:
- accessing the internal method from test projects
- the mocking of an internal interface in tests
The solution wasn’t difficult and writing it down seems like the good start of a new year.
1. Accessing the internal method from Tests project
If you try to access internal void InternalMethod() from test assembly (which I assume is different than assembly where method exists), the compiler will give you following error:
'InternalMethod()' is inaccessible due to its protection level
What you can do is either change the method back to being public (obviously not what we want) or add the following line at the beginning of any file in the assembly where method exists:
The interesting thing is that this line is global for entire assembly (which is obvious when you read it). There is no need to copy it to every file with internal class or method.
Furthermore, you can narrow down assembly and provide it’s Public Key Token as an additional security measure, but I don’t think it can actually prevent a skilled hacker from accessing them for long.
2. Mocking of internal interface
If you try to use Moq library to mock internal interface you will be facing even longer error message (but with the partial solution already embedded inside):
Castle.DynamicProxy.Generators.GeneratorException : Can not create proxy for type Moq.IMocked`1[[IInternalInterface, InterfaceAssembly, ...]] because type InterfaceAssembly.IInternalInterface is not accessible. Make it public, or internal and mark your assembly with [assembly: InternalsVisibleTo(InternalsVisible.ToDynamicProxyGenAssembly2)] attribute, because assembly Moq is strong-named.
It would seem that simply adding
[assembly: InternalsVisibleTo("InterfaceAssembly.Tests"), InternalsVisibleTo(InternalsVisible.ToDynamicProxyGenAssembly2)]
would solve the problem, but that didn’t happen in my case. On the contrary, the error message because much more obscure:
System.ArgumentException : Type to mock must be an interface or an abstract or non-sealed class. ----> System.TypeLoadException : Type 'Castle.Proxies.ObjectProxy' from assembly 'DynamicProxyGenAssembly2, ...' is attempting to implement an inaccessible interface.[/code]
As it turns out, the static variable InternalsVisible.ToDynamicProxyGenAssembly2 embeds Public key token that doesn’t match the one on DynamicProxyGenAssembly2. Therefore I was required to fall back to just assembly name
[assembly: InternalsVisibleTo("InterfaceAssembly.Tests"), InternalsVisibleTo("DynamicProxyGenAssembly2")]
And henceforth my tests are running and users are not overwhelmed by the abundance of internal (and therefore a bit useless) classes.