A small code kata to practice dependency injection in C# using Ninject.
- Implement an IoC container using Ninject
- Inject all dependencies throughout the application
- Improve testability throughout the application
In this kata, you will perform several refactorings to achieve the goals stated above. I will guide you through the following steps:
- Refactor
Generator
class to inject dependency - Changeset - Configure a Ninject kernel with appropriate bindings - Changeset
- Move bindings into a Ninject Module - Changeset
- Extract App from
Program
- Changeset - Refactor tests to mock injected dependencies - Changeset
- Test drive the addition of a dependency binding - Changeset
Steps 4 and later are not required for dependency injection, but are merely benefits of it. Step 4 allows all dependencies (including the Ninject kernel bindings) to be tested. Without this step, integration tests remain quite possible, although mocking dependencies is nearly impossible when calling Main()
from a test.
Once you've completed all exercises, I'll leave you to ponder a few other considerations:
- For new applications, when is the right time to introduce dependency injection? What about an IoC container?
- How should bindings be organized within a large application?
- How do you balance application config with bindings? Do you read config values before processing bindings, or should your types read config values themselves?
- Should libraries contain bindings for their types, or should bindings live in an executable?
- Should the full dependency graph be constructed immediately at runtime, or should some dependencies be constructed on demand when the user enters a certain section of the application?
- How might such lazy instantiation be implemented?
- What are the consequences of each option?
- When should tests use the production kernel, a test-only kernel, or no kernel? Under what circumstances, if any, should you (re)bind dependencies to mocks?