You don't need a Inversion Of Control Container in JavaScript
What sone people really love about angular, is its dependency injection feature. You register all classes to the IoC Container and when using a component, Angular will new
that class for you, injecting all its dependencies, that have been specified in the constructor to be needed.
In a test, you can create an instance of a component yourself with ‘new’ and provide the original or a mock dependency to it. Thanks to typescript, you always get help to.
Last year microsoft published a dedicated inversion of control container tsyringe. It is dedicates for framework independent node.js and client applications.
And the also the nest.js
framework, that introduces a IoC container to node.js.
Best IoC Container in node.js are Modules
When you import or require dependencies, you can directly use then and typescript know what type everything has. This way, it is possible to avoid lots of duplicated type definitions.
Buy in unit test we have to be able to inject a mock as dependency. We need a dependency injection container.
And this is the kicker: the require function is a great dependency injection container. You can overwrite dependencies yourself, or use a little utility module to inject the mock when the module require a module.
For me proxyquire
works pretty good. But most people today like the all in one solution jest better. Often I also use sinon
. It can quickly mock a single dependency function.
Javascript Module Formats
Today in 2020, a shift in javascript modules is happening. That is why some solution thay did a good job in the past has a difficult time today.
Typescript and JavaScript have now support for the new javascript modules and the import expirt feature. One part of these modules that I really don’t like, is the ‘default export and how how typescript assign this to 'module.exporta.default
. Before in node.js we have been able to export like this: module.export = function()...
. But typescript and the new js modules have a difficult time to use the new ‘import’ feature to import these features.
My advice is that’s why, to always do named exports. That can can work well across all todays environments. Hopefully, the new module format get supported my node.js very soon.
How to use Proxyquire
In your test file, if you are using mocha or any other testing frameworn, you require or import proxyquire
. Then you can use it like this:
1 | const moduleToTest = proxyquire.noCallThru().load('../path/to/moduleToTest', { |
This example is working proxyquire will behave similar to require
. When the moduleToTest
is require or import the dependency
and the test will use its functionality
that is provided in the second parameter. The functionality should have the same signature as the original.
When working with typescript with the import syntax, I found the dependency you pass into proxyquire, needs an extra field __esModule: true
.
Provide a mock via spy or fake from sinon
1 | const fakeFunctionality = sinon.fake(()=>[someExampleData]); |
What other People do
I prefer modules that do one thing well. Other people prefer using jest today. Jest is kind of a combination of different tools: nyc
, mocha
, chai
, sinon
and proxyquire
. With that I don’t say it depend on all the other modules, but is provides similar functionality from one package, trying to ease some inconsistencies and incompatibilities. Anyway, I believe extra IoC containers are not needed.
Now that you learned something good about testing, are you interested in some accessibility? a11y