Dependency Injection & Mocking
What is Dependency Injection?
This is a pattern used for decoupling components and layers in the system. The pattern is
implemented through injecting dependencies into a component when it is constructed.
These dependencies are usually provided as interfaces for further decoupling and to support testability.
The sources used for this exercise can be found on the final slide.
Pure Dependency Injection (Poor man’s DI)
Pure Dependency Injection uses constructor or property Injection where lower level components are passed using constructors or properties.
var svc = new ShippingService(new ProductLocator(),
new PricingService(), new InventoryService(),
new TrackingRepository(new ConfigProvider()),
new Logger(new EmailLogger(new ConfigProvider())));
DI container
DI Container simplifies the creation of Composition Roots especially for dependencies having complex setup scenarios.
DI Container also provides benefits like Interception (for Aspect oriented programming) for cross-cutting concerns like logging, caching security etc.
var svc = IoC.Resolve();
Comparing DI Containers
| Autofac | Dryloc | Simple Injector | Unity | ||
|---|---|---|---|---|---|
| Configuration | XML/Auto | Auto | XML/Auto | ||
| Custom Lifetimes | Yes | Yes | Yes | ||
| Interception | Yes | Yes | Yes | ||
| Auto Diagnostics | Yes | ||||
| Speed | Average | Fast | Fast | Average |
Pros & Cons
| Advantage | Disadvantages | |
|---|---|---|
| Pure DI | Easy to learn | |
| Strongly typed | ||
| Rapid feedback | High maintenance | |
| DI Container | Low maintenance | |
| Convention registration | ||
| Xml registration | Weakly typed | |
| Harder to learn | ||
| Slower feedback |
DI Container Capabilities to Consider
- List/array dependencies: When several registrations of IService exist in a container, many frameworks automatically provide IService[] / List.
- Autofac/DryIoc/SimpleInjector have a better support than Unity
- Open Generics: Service<> can be registered as IService<>, and then any request for IService should be resolved with Service.
- Optional parameters: Optional parameters can be used to specify an optional dependency
- Autofac/DryIoc have better support than SimpleInjector/Unity
- Func & Lazy support: When TService is registered in a container, many frameworks automatically provide Func<..., TService> & Lazy
- DryIoc > Autofac> Unity > SimpleInjector
- Covariance & Contravariance: Varying capabilities with different containers.
Unity
Unity was originally developed by Microsoft, though it is no longer supported by Microsoft and it is currently maintained by the community. Unity allows for configuration using both code and xml; configuration of the containers is similar and swapping them out is a fairly straightforward task. We are using Unity for our examples.
Unity with XML configuration/ServiceLocator
// Code
UnityContainer container = new UnityContainer();
// load xml configuration
container.LoadConfiguration();
// register with the service location
ServiceLocator.SetLocatorProvider(() => new UnityServiceLocator(container));
// resolving using the service locator
IClient client = ServiceLocator.Current.GetInstance();
// Configuration using Code
container.RegisterType<IService, SomeService>();
Container.RegisterType<IClient, SomeClient>();
// Resolving using the container instead of Service Locator
IClient client = container.Resolve();
Unity Configuration-XML
Unity Interception
Mocking
The correct way of unit testing is to isolate the behavior of the object we want to test from its external dependencies. This way we can get easily and comprehensively test all code paths to verify system behavior.
Mock is an object that simulates the behavior of a real method/object in controlled ways.
We replace the other objects by mocks that simulate the behavior of the real objects.
Moq
Moq is a free mocking library that helps in writing tests; it has a clean fluent interface that makes it easy to use.
var tester = new Mock();
// Setup a callback for a void method.
tester.Setup(t => t.Void(It.IsAny())).Callback(s => voidArg = s);
// Setup the result of a method.
tester.Setup(t => t.Bool()).Returns(true);
// Setup that throws an exception
tester.Setup(t => t.Exception(It.IsAny)) .Throws(new Exception());
// Ensure that a function was called.
tester.Verify(m => m.Void("A"), Times.Once);
// Ensure that a function was NOT called.
tester.Verify(m => m.Void("B"), Times.Never);
AutoFixtures
AutoFixtures helps in the setup of data for unit tests; this way developers can focus on data that is tested rather than how to setup the test scenario, by making it easier to create objects graphs containing test data.
// Fixture setup with Moq integration
var fixture = new Fixture().Customize(new AutoMoqCustomization());
// Freeze returns the same Mock implementation, the one with expectations
Mock mapMock = fixture.Freeze<Mock>();
// Creates an anonymous variable of the type ClassA
// If the constructor below is parameterized that takes in an implementation of IDAL, it will use
the frozen mock object along with other parameters
ClassA objectA = fixture.Create();
Shouldly
Shouldly provides better assertion than what the testing frameworks provide.
Improved test code readability.
Shouldly-contestant.Points.ShouldBe(1337);Test Framework -Assert.That(contestant.Points, Is.EqualTo(1337));
Better test failure messages.
Shouldly-contestant.Points should be 1337 but was 0Test Framework -Expected 1337 but was 0
Resources
Dependency Injection & Mocking – code samples