11 June 2010

Making internals unit testable

When creating - or attempting to create ;-) - reusable components, it’s common practice to hide the gory details of your implementation – not only on class level, but also on assembly level. The drawback of this approach is that you cannot write unit tests to test low-level operations of your component – invisible to the world means invisible to your assembly with unit tests as well.

One approach is to make the component that is to be tested public after all – I admit I did it a couple of times in my wee days before I learned of this trick that actually dates back to .NET 2.0 if I am correct.

Let us assume you have a library MyCompany.GreatObjects.dll, in which you have a component MyCompany.GreatObjects.Internals.MyInternalComponent, and a test library in MyCompany.GreatObjects.Test.dll.

1. Strongly name your test project

Say what? No, I am getting older but I am still not senile (or at least no-one dared to tell me yet). If you don’t know what I mean with strong naming, follow the procedure described here. You might want to strongly name your project to be tested as well, when you are at it ;-) And then build your solution again.

2. Open a Visual Studio Command Prompt

Open the command prompt belonging to the version of Visual Studio you are currently using to develop and test your components. Navigate to the bin/debug subdirectory of your MyCompany.GreatObjects project.

3. Retrieve the public key of your component

Enter the following command:
sn –Tp MyCompany.GreatObjects.dll

This will give two answers – the first one, preceded by the text “Public key is” is the one you are after. It will span something like four and a bit lines and look like this:
0024000004800000940000000602000000240000525341310004000001000100dfb0f05568ca56
3e3ae7449a48d8060be86bc091a88e915a29270a43417aa82c73dea3184beab7e4dfdfca865380
dcda1d8ca2c472b5bd7f889a0f5e77d5ec0b9990a4ca03fb71c881a51585416b1e58be6da0875b
42a2d754911fbfa1daa60884c6a6ff14f636530e197c61310f622794b0f4fbd36ade16b9ea6fa3
b850febd

Copy that code into a text file and carefully remove all line breaks, so that all these characters end up in one line. Take care not to remove any other character or your name is mud.

4. Edit the AssemblyInfo.cs of the component project

This file sits in the “Properties” application folder of your MyCompany.GreatObjects project.  Add the following line (where does not matter, but the end is a logical place):
[assembly: InternalsVisibleTo("MyCompany.GreatObjects.Test, PublicKey=****")]
replace the **** by the actual value for the public key you retrieved – on one line.
Rebuild the MyCompany.GreatObjects project, and you are done. You can now test MyCompany.GreatObjects.Internals.MyInternalComponent and other objects marked as internal, of course - from MyCompany.GreatObjects.Test. But only from MyCompany.GreatObjects.Test. For the rest of the world the internals are still safely invisible.

Purists might say that you only need to test external behavior of your component, but I can think of plenty of scenarios in which you want to change the working of an internal piece of code and still want to be able to verify its behavior – internal or not.

Credits go to my colleague Valentijn Makkenze for pointing this out to me first