Mark Gilbert's Blog

Science and technology, served light and fluffy.

With a little help from my Friends – TDD, Mocking, and InternalsVisibleTo

My current project is building a .NET library that will interface with multiple different web services.  Some of those services were not ready when I started the library, and I wanted to push myself further into mocking, so I wrote a .NET interface, and a wrapper class for each web service.  That allowed me to mock the services out, and simulate the response for a given request.  Once the web service became available, I’d implement that interface, and pass the requests through to the real services.

The goal here was to completely abstract the actual web service calls and responses from the user of the library.  However, in order for NUnit to be able to test those interface and other classes, they had to be declared Public.  That meant that someone actually using the library would see all of that structure in Intellisense – even when they would never use it, and would probably be confused by it.  This was the unfortunate tradeoff of TDD – or so I thought.

One of my colleagues, Doug, found a little assembly attribute called InternalsVisibleTo.  Applying this to your assembly (in the AssemblyInfo.vb/AssemblyInfo.cs class) allows non-Public members to be visible to the specified external assembly.  That allowed me to change the declaration on the Public classes that I didn’t really want exposed to a consumer of the library to Friend (the not quite equivalent to C#’s "internal" declaration).  That meant that I could effectively expose those classes and other items to my test assembly, but hide them from every other assembly.  For more information on this attribute, check these links out:

http://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.internalsvisibletoattribute.aspx#Y1557
http://devlicio.us/blogs/derik_whittaker/archive/2007/04/09/internalsvisibleto-testing-internal-methods-in-net-2-0.aspx

(Please note, the second is an older post by Derik Whittaker, and at the time he wrote it this was only available to C# assemblies; that has since been remedied – you can use it in VB now as well.)

So I added a line to expose this to my test assembly.  Then I started systematically changing Publics to Friends, recompiling, and re-running the unit tests.  I ran into a few bumps along the way.

First, I was doing a lot of constructor injection in association with the mocking where the parameter-less constructors would set up the lower-level objects, but the other variants would allow me to pass those objects in (the passed-in objects would be my mock objects).  In the course of this rework, I ended up hiding a lot of those classes.  Initially, the constructor variants that used them were marked Public, which the compiler had a fit about – I couldn’t expose those classes via the constructor parameters because the classes were now marked as Friend, but the constructors were marked as Public.  Changing the constructor designations to Friend solved this.

Second, when I started changing the classes used by my mocking framework, Moq, I found that the InternalsVisibleTo line allowing my test assembly wasn’t enough.  I figured the Moq assembly needed to be explicitly allowed, too.  I tried the code-roulette approach first, without success.  Then I consulted the internets, which of course had the answer.  Andrey Shchekin had the solution – DynamicProxyGenAssembly2 (http://blog.ashmind.com/2008/05/09/mocking-internal-interfaces-with-moq/).  Yeah, that was totally going to be my next guess.  Uh-huh.

So, I have my mocking/TDD cake and get to eat it too.  The library footprint is nicely trimmed back, without much of the original clutter, but I can still unit-test to my heart’s content.  Many thanks to Doug for finding this little gem!

The only thing I wasn’t able to accomplish with this was hiding the SOAP web service structure.  I tried changing the Visual Studio-generated classes to Friend, but that started failing when I tried to call the service.  Perhaps there is another attribute that at least hides these from Intellisense.  A search for another day.

Advertisements

December 7, 2011 - Posted by | Visual Studio/.NET

Sorry, the comment form is closed at this time.

%d bloggers like this: