Fixture Setup With Installers
It's no secret that I prefer unit tests over integration tests. Whenever it's possible to replace an external resource dependency with a test double, I do that, but sometimes, you need to test the resource access component itself: If you have a data access layer, at some point you should test the data access component itself to ensure that it behaves correctly in relation to the database, etc.
One of my main integration testing principles is that fixture setup and teardown should be fully automated. This means that writing integration tests includes developing code that sets up and tears down the fixture. If you stop and think about this for a moment, what you are actually doing is creating an automated installer for your SUT. If this is the case, why not move the appropriate setup code to the SUT itself? This is exactly the approach I've recently begun to take, as you can see in my post about performance counter testing. In this post, I used an existing Installer class (PerformanceCounterInstaller) to define how to set up performance counters for the SUT.
As long as you need to install performance counters, MSMQ queues, event logs or Windows services, you can use the appropriate Installer classes contained in the BCL itself; in any other case, you'll need to create a custom Installer by deriving from Installer or ComponentInstaller.
The nice thing about Installers is that they are so darn composable. From a test perspective, you can use the SUT's Installer as a starting point for setting up and tearing down your fixture:
If, in your fixture setup or fixture teardown logic, you need to perform additional steps apart from the logic defined in the SUT's Installer, you can create an optional, test-specific Installer that includes the SUT's Installer in its collection. If you don't need to perform additional steps, you can just use the SUT's Installer as is.
In any case, you can execute the Installer during fixture setup and teardown using TransactedInstaller.Install and TransactedInstaller.Uninstall, which causes the SUT's Installer logic to be executed.
A benefit of this is that you can now reuse the SUT's Installer together with other components' Installers to compose an Installer for your application:
Each component that makes up your application has its own Installer (if it has an installer at all). When you compose all the components into the top-level executable (possibly using code as configuration), you also compose an Installer for the application by adding all the components' Installers to the application's Installer. The top-level executable is just a humble executable containing what we could call a Humble Installer.
You would typically use this approach to create a shared fixture, but nothing stops you from creating persistent fresh fixtures using the same technique, although it's probably going to slow down your tests. Normally, I use this Installer-based methodology to create an immutable shared fixture, and then use custom test code to set up the rest of the fixture.
my next a future post, I'm going to provide a new example that demonstrates this approach. Update: The example is now available here.