trackabout/speceasy — pull request #6 by mmertsock (2013-08-02 20:19:24+00:00)
Title: Replace Ninject with TinyIoC

### Why

We have been using Ninject as an internal IoC container for the auto-mocking and dependency injection features of the generic `Spec<>` class. The Ninject dependency, however, adds *three* NuGet dependencies to SpecEasy:

1. Ninject
1. Ninject.MockingKernel
1. Ninject.MockingKernel.RhinoMock

That's a lot of dependencies.

### Implementation

[TinyIoC](https://github.com/grumpydev/TinyIoC) is a relatively simple and lightweight IoC container. It's designed as a single code file that can either be included via NuGet or copied as source into a project file.

I opted to put a copy of TinyIoC.cs into the SpecEasy solution, at least for now, not just to reduce the number of dependencies (one dependency on TinyIoC is a lot better than three Ninject dependencies), but also because there are some significant enhancements to TinyIoC that were necessary, so this will get us going until we decide whether/when to get a pull request completed for the main TinyIoC project.

There were two changes to TinyIoC required to make it work with SpecEasy:

1. When resolving dependencies on a concrete type, register the resolved dependency as a singleton object in the container so that future requests to resolve that type return the same object instance. This is required so that `SUT` or its concrete object dependencies are only instantiated once during a given unit test. The container is reset at the beginning of each test so these registrations are not persistent.
2. Provide a way to run some custom code when TinyIoC fails to resolve a dependency. This is used to automatically generate mock instances of any interface or abstract type dependencies using RhinoMocks.

These two enhancements are implemented as optional properties of an `options` param in the `Resolve` method: an `UnregisteredResolutionRegistrationOptions` enum type and a `FallbackResolutionAction` callback function.

Both of these behaviors were previously provided by the MockingKernel Ninject extensions.

### Impact

There should be no impact on existing (reasonable) usages of SpecEasy. The Ninject container implemented in the generic Spec class was indeed exposed as a protected property (named `MockingKernel`), but it was never meant to be used directly by test subclasses. Instead, the subclass is meant to use the `SUT`, `Get`, and `Set` members, and to let the auto-mocking and DI behavior "just work".

These changes will further enforce this design by giving the `MockingContainer` member `internal` visibility, and declaring all of the TinyIoC classes and types as `internal`.

There could be a risk of unit tests breaking if they expose some sort of edge case with the auto-mocking or DI behavior that works differently in TinyIoC. Everything looks good though after trying this against a suite of hundreds of tests.