Those people that know me know that I have every respect for the concept of Unit Testing code. They also know that I have some issues with the implementation of unit tests where they interfere with the day to day operation of my code. I struggle with the concept of changing my production code purely to make it “testable” when those changes may work to make the code less reliable.
In particular, I like to keep scope as restricted as I can and like to stick with private and internal classes, only making a class public if it is part of the public API I want to expose. That works great and ensures that people are no using the API inappropriately, but makes the code difficult to test. In order to test my internal classes, I have to be able to access them from the test project and to access them from the test project, I have to make them public. I make them public and I have violated my rules on minimum scope.
My “mentor” in TDD and unit testing tells me this is Ok as being able to run automated tests is way more important than scope. I beg to differ, especially when the API I am creating may be used by someone else.
Having found myself in this dilemma again recently, I did a little research – surely this has been dealt with by now. Turns out it has and it turns out the solution is somewhat elegant in its simplicity.
My main application is called “AppStart”. This exposes a very small API to plug-ins that need to understand the limited number of methods and properties I want to expose. The internals of the project need to remain protected, so I have coded those as “internal” classes. My test project is called “AppStartTest” and contains references to the project I am trying to test.
In order to allow AppStartTest to access and run tests against AppStart, I added the following line of code to the AssemblyInfo.cs of the AppStart project:
And that’s it. Clean and simple and my test project can now see the internal classes of the project I am trying to test.
I get to retain my restricted API for my users and, at the same time, get to exercise my internal classes.
This works for classes marked as internal or friend and you can specify more than one assembly within the same attribute if you have split your tests in to separate projects.
There is no equivalent for private, but I can live with that as this is an enormous step forward.