I'm learning about dependency injection and while I can see the appeal of it when writing functional libraries, I fail to see how it solves anything when you'll also be the one using the libraries.
It makes testing the library much simpler, because there isn't much to test.
But you will eventually have to test your injected function when you use the library and have to deal with mocking and stubbing functions from the standard library.
This is a concrete case I'm dealing with in Node.js:
function compile(options) {
var files = options.files;
var texCompiler = options.texCompiler;
var pdfMerger = options.pdfMerger;
return Promise.all(files.map(texCompiler(files)))
.then(pdfMerger);
}
That is trivial to test: injecting mock objects or spies as the texCompiler
and pdfMerger
is a piece of cake because the function really doesn't do much at all. All I can test is that both functions are called in the right sequence.
It doesn't save me from testing my texCompiler
and pdfMerger
functions eventually though. They look something like that:
var tex2Pdf = Promise.method(function tex2Pdf(tex_doc) {
var latex_command = 'pdflatex';
var pdf_output_filename = path.parse(tex_doc).name + '.pdf';
var cmd = latex_command + ' ' + tex_doc;
var options = {
cwd: path.resolve(tex_doc, '..') // pdflatex will only look for custom
// cls files in the cwd and includes relative to the cwd
};
child_process.spawn(cmd, options)
.on('end', function() {
console.log('tex2Pdf finish');
debugger;
return path.resolve(tex_doc, '..', pdf_output_filename);
})
.on('error', function(e) {
throw e;
});
});
var mergeTwoPdf = Promise.method(function mergeTwoPdf(pdf_files) {
var output_file = randomId() + '.pdf';
var cmd = 'gs -dBATCH -dNOPAUSE -sDEVICE=pdfwrite -sOutputFile=' + output_file + ' ' + pdf_files[0] + ' ' + pdf_file[1];
child_process.spawn(cmd)
.on('finish', function() {
return output_file;
})
.on('error', function(e) {
throw (e);
});
});
This is the real thing, and it is a bigger pain to test. I have to mock child_process.spawn
with a spy to make sure it gets called with the right arguments, but it doesn't actually do anything, because I don't want to actually merge any PDF file when running the tests and my mocks have to emit the right events so the function isn't stuck.
These are problems I would have had if I didn't inject the dependency in my first snippet and used these functions instead. And it really feels like I'm pushing the problem further down without solving it.
Am I misunderstanding dependency injection? Am I doing it wrong?