Testing without compromise using InternalsVisibleTo

Have you ever wanted to test the behaviour of a method, without making it part of your public API? Have you ever been forced to choose between compromising on the encapsulation of your business logic, and ensuring that it behaves as expected? Allow me to introduce the InternalsVisibleTo attribute! In this post, we’ll look at what this lets you do, but we’ll also touch briefly on the debate surrounding its use.

InternalsVisibleTo is one of those lesser known features of .NET, but it’s been available since .NET 2.0, which was released 15 years ago.

So what does InternalsVisibleTo actually do? Exactly what it says on the tin: it allows you to delegate access to objects marked internal to other assemblies. As you’ll remember, internal is an access modifier which ordinarily restricts access to callers within the same assembly. By including an InternalsVisibleTo attribute in our AssemblyInfo.cs file, we can allow code in specific external assemblies to access internal members as if they were actually located in the same assembly.

This might seem like an esoteric piece of .NET functionality but is a godsend when it comes to testing. How many times have we wanted to test something that we don’t want to make public to every consumer of our code? Using InternalsVisibleTo means that we can keep our public API clean, whilst retaining the ability to test some of our encapsulated logic.

Black box vs white box

Now, there is a school of thought that says that only the public API should be tested. The thought process is that only your outputs to the rest of the world matter: as long as the externally observable behaviour of your code matches the behaviour described in the specification, you’ve done your job. The other side of this coin is that testing implementation details can lead to brittle tests, which mean you may spend too long maintaining them. Two excellent cases for “black box testing” – treating your code as a singular mechanism for translating inputs into outputs.

On the other hand, I often find myself encapsulating such complex functionality that testing the publicly available API is unhelpful. For instance, I may only expose a few top-level methods that each call a number of internal methods to perform the “heavy lifting”. A pure black box approach may mean that test failures are hard to trace back to the underlying regression, so at times like these, I like to indulge in a little bit of “white box” testing.

“White box” testing is simply the testing of logic normally contained within a library, invisible to consumers of your code. If used sparingly, on key methods used within your projects, a spot of white box testing can be a lifesaver. Don’t be tempted to start testing all your internal methods though, as this can quickly lead to unpleasantly tightly coupled tests that break whenever you refactor your code.

Peeking inside

Besides InternalsVisibleTo, there are a couple of other ways to conduct white box testing. The first (alluded to above) is to compromise on the encapsulation of your code and simply make everything you want to test public. This can lead to a very confusing experience for consumers of your code, which may include your colleagues or even your future self. How will they know what they’re supposed (or allowed) to do when presented with 101 methods?

The other approach is to use reflection. Essentially, this involves querying your assembly to work out what it contains. Once we know the contents of an assembly, we can invoke whatever we like:

Whilst this works, it’s a bit verbose for my taste. With the internals visible to the caller, four lines of pretty idiosyncratic code can become two lines that even a Java developer would understand:

Yes, I really did just include that code…

Summary

If you’d like to see InternalsVisibleTo in action, check out my demo repo. It contains two test projects that target a single library project, which itself contains one public method and one internal method. Each test project tests both methods, but only one has access to the internal method directly. Try downloading the code and running the tests. Note the AssemblyInfo.cs file in the library project and the reflection example in the test without access.

For me, InternalsVisibleTo is a pragmatic answer to a problem that attracts dogma. Yes, there are good reasons to limit the amount of “white box” testing. Sometimes, however, it’s the sensible thing to do. Yes, there are other ways to access the internal (or even private) members of an object. Reflection even makes this possible whilst retaining a cleanly encapsulated API. But why choose verbosity when InternalsVisibleTo offers simplicity?

Finally, I should probably acknowledge that there is nothing limiting your use of InternalsVisibleTo to your unit tests. If you’ve got an example of its use for something other than testing, let me know in the comments.

Futher reading

Leave a Reply