At
DrDoctor we have a reasonable set of Spec tests using
SpecFlow. These tests tend to be a lot more involved than our unit tests as they always exercise most (close to all) of an application - and sometimes even multiple applications that make up portions of the entire system. We don't have hard and fast rules about how much of the system a set of specs should cover, but they tend to end up as fairly significant integration tests, almost always hitting the database for example.
As these specs tend to be written prior to the implementation* we often start with dependencies mocked out and as we implement the system under test move to real objects. For instance, we might start with a mock IGetThatObjectYouNeed data access class that ends up being implemented as part of the functionality we are building.
However, once the functionality is built, we finish in a situation where the component we have been working on has its dependencies composed using an IoC container (
Castle Windsor) and the specs build up the required dependency graph manually. While that's not catastrophic, we really like our specs because they show up problems with an application in a way that unit tests don't, so knowing that the application is composed in the same way in the specs and in the real world also helps us sleep easy. It's kind of like the difference between knowing that all the parts of a car work and knowing that all the parts work and they've been put together the right way.
A fairly simple way to do this is to use Castle's
fluent registration to call your application's installer e.g.
var container = new WindsorContainer().Install(new YourApplication.Installers.YourInstaller());
var sut = container.Resolve<ApplicationEntryPoint>();
A problem with this though is that it may use every concrete implementation that your application requires, which might not be what you want for tests that are run many times a day. For instance if your application sends SMSes via a 3rd party service, you probably don't want your automated tests using that.
In order to avoid this, you can have multiple installers in Windsor and then only call the installers you need. We have a separate BusInstaller for exactly this reason, so that we can use a different implementation in our tests. Having said that you probably want your installers separated out into areas that make sense for your application, not that make sense for your specs. Also, you probably don't want to have to change your installers because of modifications to your specs.
In order to avoid using specific implementations that are registered in an installer that you don't want to modify, you can use another feature of Castle Windsor that allows you to register multiple implementations of a type and declare one as default (http://docs.castleproject.org/Windsor.Registering-components-one-by-one.ashx)
Thus you might have an installer that looks something like this:
public class MyComponentInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(
Component.For<IDoSomethingYouCareAbout>().ImplementedBy<SpecificImplementation>(),
Component.For<IStuffRepository>().ImplementedBy<StuffRepository>(),
Component.For<IHitAnExternalResource>().ImplementedBy<ServiceYouDoNotWantToHit>(), // don't want this to be used in the specs
Component.For<IAmAlsoUsefulToTest>().ImplementedBy<AnotherSpecificImplementation>(),
Component.For<IAmAnotherRepository>().ImplementedBy<AnotherRepository>(),
Component.For<ApplicationEntryPoint>());
}
}
Then in your spec wherever you set up your system under test:
var container = new WindsorContainer().Install(new MyApplication.MyComponentInstaller());
container.Register(Component.For<IHitAnExternalResource>().UsingFactoryMethod(Mock.Of<IHitAnExternalResource>).IsDefault());
systemUnderTest = container.Resolve<ApplicationEntryPoint>();
The second line, using 'IsDefault()', means that rather than having to remove the 'ServiceYouDoNotWantToHit' from the container, we override it with our mock (we're using MOQ, if you're wondering about the syntax), by setting it as the default implementation that Castle returns.
In this way you can avoid whichever concrete implementations you want, but still have quite a high level of confidence that your actual application is composed correctly.
*we're not purist BDDers, but it's a style that works well for us a lot of the time.