Well now there is FluentAssertions.Aufofac which supports test the DI configuration like this
container.Should().Have().Registered<SuperCoolService>() .AsSelf() .As<ICoolService>() .Singleton(); container.Should().Have().Registered(superCoolInstance); container.Should().Have().Registered<OtherCoolService>() .Named<ICoolService<("prettyCool"); container.Should().Have().NotRegistered<UncoolService>("uncool");
And not even registration but also resolving which asserts that you did not forget any dependency
But maybe there is just no need to test Dependency Injection? Let's see...
In general, the more you apply Dependency Injection (DI) the easier becomes unit testing and Test-driven Development (TDD).
This is because the complexity of constructing all dependencies is shifted to the so called Composition Root, i.e. the place where you "wire up" and configure all your dependencies.
Undoubtedly, the best way to do this is by using some Inversion of Control (IoC) container.
With an application growing in complexity, there is also growing need to organize and test the IoC configuration.
Here is the usual story: During release sprints we typically develop features in parallel that form complete workflows (think epics or themes, if you will). While carving these out, we mock out dependencies to the other features. This way everyone can move forward without needing the other features completed.
For example, you're designing the coolest recommendation view on the planet. While your buddy is on the recommendation service on the backend, you will register a mock, i.e.
... builder.RegisterType<MockRecommendations>().As<IGetRecommendations>(); // builder.RegisterType<RecommendationService>().As<IGetRecommendations>(); ...
Once your buddy gives thumbs up for integration testing you switch to the real thing. Finally, you finish and move on to the next feature.
So far, so good. But then, a critical issue with your view (or likewise viewmodel, controller) rises in production. No problem, you go for the hotfix, switch back to your mock so you can better and quicker reproduce the problem. You fix the problem but then, you forget to switch back from the mock to the productive recommendation service - which gives you a serious #facepalm.
Wouldn't it be nice if there was a test saving you from breaking the release next time? Well, now there is.
Most of the time, we prefer using this stack
- Autofac for wiring up DI,
- NSubstitute for mocking and
- FluentAssertions for extremely readable tests that naturally explain when failing.
In the rare case that you - as a developer - never quoted that "Code is read much more often than it is written" and are wondering why you should thrive for readable code in the first place, then here is a hand-picked selection of classic references that we found to be helpful
- What Do Programmers Really Do Anyway? (Peter Hallam, 2006)
- When Understanding means Rewriting (Jeff Atwood, 2006)
- Code is read much more often than it is written, so plan accordingly (R.Chen - MSFT, 2007)
- Clean Code: A Handbook of Agile Software Craftsmanship (Robert C. Martin et al., 2008)
- The Futility of Commenting Code (Bob Carpenter, 2009)
- Learn to Read the Source, Luke (J.Atwood 2012)
- Review of M.Fowlers classic "Refactoring: Improving the Design of Existing Code" (2012)