Before we start: While this post applies to Inversify 7, it’s concepts can be applied to other versions as well.
The Problem
Quality has been promoted for a long time in practices such as TDD or Clean Code. Finding better and more ways to test our code is paramount. With increasing complexity it doesn’t get easier, so we should take every opportunity to improve testability, to add tests and as a result improve the quality of our code before it is deployed to production.
In this article I present an option for testing the Inversion of Control (IoC) container that Inversify offers. I used hacks in versions prior to version 7, e.g. by accessing internal data structures of the Inversity package.
Why would I want to test the bindings? Typically, classes that can be injected or are controlled by Inversify need to be registered. In its simplest form a registration binds an interface to a class. The registration is done using decorators. It is easy to miss or to use the wrong decorators.
Therefore, I prefer being able to test:
Is the specific binding present?
Are there only the specific bindings?
When resolving for a service identifier, does the IoC container return the expected instance?
All of these can be tested, helping to reduce the possibility that a decorator is missing or incorrect.
The problem is that starting with version 7, Inversify is now using private class field as per the ECMAScript specification, e.g. “#bindingService”. There is no safe, let alone standard way to access those fields.
This article describes a way of adding this testability. As a side effect, it also provides a level of insulation between Inversity and TSOA. Over the last few years, I have encountered a couple of temporary incompatibility issues between particular combinations of versions of these two packages. Each time I added a custom IoC container which I removed again, once both packages caught up again. The solution presented here makes that insulation layer permanent.
Solution
Essentially the solution is pretty simple: We just wrap Inversify’s container inside a new class. Then we can track all bindings through that wrapper class. Let’s call that wrapper class TrackingContainer.
Here is the basic idea (full source code at the end of this post):
export class TrackingContainer {
private container: Container;
// Registry mapping service identifiers to an array of recorded binding details.
private registry = new Map<ServiceIdentifier<unknown>, any[]>();
constructor(container?: Container) {
this.container = container || new Container();
}
...
}
This introduces two private member variables:
“container” which will receive the instance of the Inversify container
“registry” which is a Map object that we’ll use to record all bindings
One more consideration is that we don’t want to lose the fluent interface that Inversify offers, so we need to make sure that the method signatures of the TrackingContainer match those of Inversify’s container. Here is an example of how we can achieve this:
Keep reading with a 7-day free trial
Subscribe to GeekCoder Journal to keep reading this post and get 7 days of free access to the full post archives.