What follows is a pretty shameless abuse of one of my favorite ES6 features, default parameters. They're a simple idea that other languages have enjoyed for decades, and I am all too happy to see JavaScript join the party.
const logValue = (object = {}) => {
console.log(object);
};
logValue(); // logs: {}
logValue(5); // logs: 5
Beautiful! Reading deeper into the docs reveals that we can set more than static data as a default parameter. We can set entire expressions:
const logValue = (value = 1 + 1) => {
console.log(value);
};
logValue(); // logs: 2
logValue(5); // logs: 5
Neat! Your logic can be pretty much anything; the function above is effectively interpreted as:
const logValue = value => {
value = value === undefined ? 1 + 1 : value;
console.log(value);
};
...But let's stick with default parameters; they're a little cleaner.
So now that we have this handy tool at our disposal, what sort of mad science could we get up to with it? Why, dependency injection, of course! DI is super handy for testing code, and there is no shortage of contortions and libraries to achieve in JavaScript what other languages have as a first-class feature.
Let's say that we need to test a function that depends on some window
variables to work. It's not ideal, but we've all been there:
const leave = destination => {
window.location = destination;
};
If a unit test calls this in a browser environment, the browser will navigate to destination
and the tests will never complete. Bummer! But we can easily inject a stub window
object that won't touch the global state:
const leave = (destination, _window = window) => {
_window.location = destination;
};
So now leave
will only use window
unless an alternative value is not provided. We can write our test like:
describe('leave', () => {
it('goes away', () => {
const win = {};
leave('http://jort.technology/', win);
assert.equal(win.location, 'http://jort.technology/');
});
});
Success; the test validates that the function works and window
remains unchanged!
Because a default parameter can be anything, this is a simple way to stub modules or libraries that a function might depend on without needing to add complex infrastructure to your project.
This isn't a fit for every project, but it is a simple way to get dependency injection with minimal infrastructure overhead!