¿La inyección de dependencia no empuja la carga de la prueba más abajo en la cadena?

9

Estoy aprendiendo acerca de la inyección de dependencia y, aunque puedo ver su atractivo al escribir bibliotecas funcionales, no veo cómo resuelve nada cuando tú también serás el que use las bibliotecas.

Hace que probar la biblioteca sea mucho más simple, porque no hay mucho que probar.

Pero eventualmente tendrá que probar su función inyectada cuando use la biblioteca y tenga que lidiar con las funciones de burla y apisonamiento de la biblioteca estándar.

Este es un caso concreto con el que estoy tratando en 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);
}

Es algo trivial probar: inyectar objetos simulados o espías como texCompilery pdfMergeres pan comido porque la función realmente no hace mucho. Todo lo que puedo probar es que ambas funciones se llaman en la secuencia correcta.

Sin embargo, no me salva de probar mis funciones texCompilery pdfMergereventualmente. Se ven algo así:

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);
  });
});

Esto es real, y es un dolor más grande de probar. Tengo que burlarme child_process.spawnde un espía para asegurarme de que se llame con los argumentos correctos, pero en realidad no hace nada, porque no quiero fusionar ningún archivo PDF al ejecutar las pruebas y mis simulaciones tienen que emitir el eventos correctos para que la función no esté atascada.

Estos son problemas que habría tenido si no inyectara la dependencia en mi primer fragmento y utilizara estas funciones. Y realmente parece que estoy empujando el problema más abajo sin resolverlo.

¿Estoy malinterpretando la inyección de dependencia? ¿Lo estoy haciendo mal?

cargado de resortes
fuente

Respuestas:

8

Parece que te estás perdiendo el punto de prueba.

Eso es trivial de probar: inyectar simulacros o espías como texCompiler y pdfMerger es pan comido porque la función realmente no hace mucho. Todo lo que puedo probar es que ambas funciones se llaman en la secuencia correcta.

¡Increíble! Si la función está haciendo un trabajo trivial, entonces la prueba debe ser trivial. Del mismo modo, si todo lo que está haciendo esa función es llamar a A y luego a B, no está validando gran parte de su código.

El objetivo de las buenas pruebas es aislar lo que está probando, por lo que cuando la prueba falla, puede saber mejor qué salió mal. La inyección de dependencia ayuda a eso.

Y realmente parece que estoy empujando el problema más abajo sin resolverlo.

Claro, en este caso no está "resolviéndolo" porque su código tiene dependencias desagradables que son difíciles de aislar. Necesita trabajar con el sistema de archivos. Debe llamar a estos procesos externos. A veces esa es la naturaleza de la bestia.

La inyección de dependencias no elimina las dependencias, solo las voltea para que pueda elegir cuáles son. Eso ayuda a aislar el código para las pruebas. Ayuda a que el código sea más flexible cuando esas dependencias cambian inevitablemente. Pero no los mata: su código aún necesita poder hacer las mismas cosas en alguna parte .

Telastyn
fuente
Eso tiene sentido. ¿Hay algo más que pueda hacer para que sea más fácil probar esas desagradables dependencias?
Springloaded
2
@springloaded - solo pragmatismo. Aislar dependencias es bueno, pero en una aplicación como esta, no tiene mucho sentido. Las pruebas de integración son peores que las pruebas unitarias (tardan más, son más frágiles, son más difíciles de vincular a una causa), pero si las pruebas unitarias no le darán mucho, puede que no valga la pena el tiempo.
Telastyn
2

Creo que es la prueba que estás malentendiendo si acaso.

Probaría la creación de su archivo PDF creando un archivo PDF y comparándolo con uno bueno conocido.

Probaría la fusión de su archivo PDF fusionando dos archivos PDF y comparando la salida con, nuevamente, un buen archivo conocido.

Ewan
fuente
2
Entonces, ¿estás diciendo que debería hacer pruebas de integración en este código pero ninguna prueba unitaria? En ese contexto, ¿tiene sentido la inyección de dependencia?
cargado el
3
@springloaded: Usted hace un punto válido. Solo diría que, solo porque DI no tiene sentido en su escenario particular, no necesariamente significa que falla en todos los escenarios. Tratar con bibliotecas externas es principalmente una historia de integración de todos modos; El objetivo de DI es hacer que esa integración sea cableable en lugar de incorporarla directamente a su código.
Robert Harvey
@RobertHarvey Claro, veo cómo DI puede ser útil fuera de mi caso de uso
cargado el
hmm diría que no es una prueba de integración a ese nivel. Si lo desea, puede generar los datos sin procesar e inyectar el bit de escritura en disco si lo desea más "unidad".
Ewan