Partial dependency injection

Examples of dependency injection in javascript

Let's take a function with 3 arguments that computes and logs a sum of two numbers

1
2
3
function logSum(a, b, log) {
log(a + b);
}

Passing a logger function as a log argument makes unit testing logSum very easy

1
2
3
4
5
6
QUnit.test('log sum', function (assert) {
var logger = function (value) {
assert.equal(value, 10, 'logging correct value');
};
logSum(logger, 7, 3);
});

Dependency injection

This pattern is called dependency injection - providing dynamic arguments to the function, widely used by AngularJs for example. Dependency injection has a run time executor which inspects a function and injects correct argument by name. Or it could be done via proxy function, like in heroin

1
2
3
4
5
6
7
8
9
10
11
12
QUnit.test('log sum', function (assert) {
var logger = function (value) {
assert.equal(value, 10, 'logging correct value');
};
var logSumProxy = heroin(logSum, {
log: logger,
a: 7,
b: 3
});
logSumProxy();
// each argument is injected
});

What if we want to bind some arguments to the function, but leave others unchanged?

Partial argument binding

We could use Function.bind binding arguments to the function in left to right order by position:

1
2
3
4
5
6
7
8
9
QUnit.test('log sum', function (assert) {
var logger = function (value) {
assert.ok(value, 'logging some value');
};
var logSumProxy = logSum.bind(null, logger);
logSumProxy(7, 3);
logSumProxy(-1, 1);
logSumProxy(100, 1);
});

Notice we performed partial binding - only providing the first argument, leaving 2nd and 3rd arguments free. Can we do the same for dependency injection?

Partial dependency injection

Yes, using heroin we can. If the dependency object does not contain an own property for an argument, it is left free and you can provide it during the execution

1
2
3
4
5
6
7
8
9
QUnit.test('log sum', function (assert) {
var logger = function (value) {
assert.equal(value, 7, 'logging correct value');
};
var logSumProxy = heroin(logSum, {
log: logger
});
logSumProxy(3, 4);
});

The cool thing is that argument for the dependency injection does not matter, we could inject log and b, leaving a in the middle free

1
2
3
4
5
6
7
8
9
10
11
QUnit.test('log sum', function (assert) {
var logger = function (value) {
assert.equal(value, 13, 'logging correct value');
};
var logSumProxy = heroin(logSum, {
log: logger,
b: 10
});
// logSumProxy(log = logger, a, b = 10) ...
logSumProxy(3);
});

Use case

A good use case for partial dependency injection is in my qunit-inject plugin. It allows one to inject properties from a module into the individual unit tests, while still leaving argument assert free to be provided by the runtime framework

1
2
3
4
5
6
7
8
QUnit.module('QUnit.assert tests WITH injection', {
a: 42,
b: 1
});
QUnit.test('injection sandwich', function (b, assert, a) {
assert.equal(a, 42, 'assert works');
assert.equal(b, 1, 'b value');
});

qunit-inject creates a proxy for each unit test function, injecting module's config, but since the config does not have assert property, the QUnit's runner sees only a proxy that expects assert argument. And if you are terribly evil, you can even inject your own assert from the module, so that QUnit's assert argument is ignored.

1
2
3
4
5
6
7
8
9
10
11
12
QUnit.module('Your assert', {
assert: {
ok: function (condition, msg) {
console.log('whatever');
}
},
a: 42
});
QUnit.test('check a', function (assert, a) {
assert.ok(a);
});
// prints 'whatever'