Drängt die Abhängigkeitsinjektion die Testlast nicht weiter in die Kette?


9

Ich lerne etwas über Abhängigkeitsinjektion und obwohl ich beim Schreiben von Funktionsbibliotheken den Reiz davon sehe, kann ich nicht erkennen, wie es irgendetwas löst, wenn Sie auch derjenige sind, der die Bibliotheken verwendet.

Dies vereinfacht das Testen der Bibliothek erheblich, da nicht viel getestet werden muss.

Sie müssen jedoch eventuell Ihre injizierte Funktion testen, wenn Sie die Bibliothek verwenden, und sich mit Verspottungs- und Stubbing-Funktionen aus der Standardbibliothek befassen.

Dies ist ein konkreter Fall, mit dem ich mich in Node.js befasse :

function compile(options) {
  var files = options.files;
  var texCompiler = options.texCompiler;
  var pdfMerger = options.pdfMerger;

  return Promise.all(files.map(texCompiler(files)))
    .then(pdfMerger);
}

Das ist trivial zu testen: das Injizieren von Scheinobjekten oder Spionen als texCompilerund pdfMergerist ein Kinderspiel, da die Funktion überhaupt nicht viel bewirkt. Ich kann nur testen, dass beide Funktionen in der richtigen Reihenfolge aufgerufen werden.

Es rettet mich jedoch nicht davor, meine texCompilerund pdfMergerFunktionen zu testen . Sie sehen ungefähr so ​​aus:

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

Dies ist die reale Sache, und es ist ein größerer Schmerz zu testen. Ich muss mich child_process.spawnmit einem Spion lustig machen, um sicherzustellen, dass er mit den richtigen Argumenten aufgerufen wird, aber er macht eigentlich nichts, weil ich beim Ausführen der Tests keine PDF-Datei zusammenführen möchte und meine Mocks die ausgeben müssen richtige Ereignisse, damit die Funktion nicht hängen bleibt.

Dies sind Probleme, die ich gehabt hätte, wenn ich die Abhängigkeit nicht in mein erstes Snippet eingefügt und stattdessen diese Funktionen verwendet hätte. Und es fühlt sich wirklich so an, als würde ich das Problem weiter nach unten drücken, ohne es zu lösen.

Verstehe ich die Abhängigkeitsinjektion falsch? Mache ich es falsch

Antworten:


8

Sie verpassen anscheinend den Punkt des Testens.

Das ist trivial zu testen: Das Injizieren von Mocks oder Spionen als texCompiler und pdfMerger ist ein Kinderspiel, da die Funktion überhaupt nicht viel bewirkt. Ich kann nur testen, dass beide Funktionen in der richtigen Reihenfolge aufgerufen werden.

Genial! Wenn die Funktion triviale Arbeit leistet, sollte der Test trivial sein. Wenn diese Funktion nur A und dann B aufruft, validieren Sie nicht viel von Ihrem Code.

Bei guten Tests geht es darum, zu isolieren, was Sie testen. Wenn der Test fehlschlägt, können Sie besser wissen, was schief gelaufen ist. Die Abhängigkeitsinjektion hilft dabei.

Und es fühlt sich wirklich so an, als würde ich das Problem weiter nach unten drücken, ohne es zu lösen.

Sicher, in diesem Fall "lösen" Sie es nicht, weil Ihr Code böse Abhängigkeiten aufweist, die schwer zu isolieren sind. Sie müssen mit dem Dateisystem arbeiten. Sie müssen diese externen Prozesse aufrufen. Manchmal ist das die Natur des Tieres.

Abhängigkeitsinjektion entfernt Abhängigkeiten nicht, sondern dreht sie nur um, sodass Sie auswählen können, was sie sind. Das hilft, Code zum Testen zu isolieren. Dies hilft dem Code, flexibler zu sein, wenn sich diese Abhängigkeiten zwangsläufig ändern. Aber es tötet sie nicht - Ihr Code muss immer noch in der Lage sein, irgendwo die gleichen Dinge zu tun .


Das macht Sinn. Kann ich noch etwas tun, um das Testen dieser bösen Abhängigkeiten zu vereinfachen?
Springloaded

2
@springloaded - nur Pragmatismus. Abhängigkeiten zu isolieren ist gut, aber in einer App wie dieser macht es nicht viel Sinn. Integrationstests sind schlechter als Unit-Tests (dauern länger, sind anfälliger, sind schwerer mit einer Ursache zu verknüpfen), aber wenn Unit-Tests nicht viel bringen, sind sie möglicherweise nicht die Zeit wert.
Telastyn

2

Ich denke, es sind die Tests, die Sie falsch verstehen, wenn überhaupt.

Ich würde Ihre PDF-Dateierstellung testen, indem ich eine PDF-Datei erstelle und sie mit einer bekanntermaßen guten Datei vergleiche.

Ich würde die Zusammenführung Ihrer PDF-Datei testen, indem ich zwei PDF-Dateien zusammenführe und die Ausgabe erneut mit einer bekanntermaßen guten Datei vergleiche.


2
Sie sagen also, ich sollte Integrationstests für diesen Code durchführen, aber keinen Komponententest? Ist in diesem Zusammenhang die Abhängigkeitsinjektion überhaupt sinnvoll?
Springloaded

3
@springloaded: Sie machen einen gültigen Punkt. Ich würde nur sagen, dass DI nicht in allen Szenarien fehlschlägt, nur weil es in Ihrem speziellen Szenario keinen Sinn ergibt. Der Umgang mit externen Bibliotheken ist ohnehin in erster Linie eine Integrationsgeschichte; Der Zweck von DI besteht darin, diese Integration verkabelbar zu machen, anstatt sie direkt in Ihren Code zu backen.
Robert Harvey

@ RobertHarvey Sicher, ich sehe, wie DI außerhalb meines Anwendungsfalls nützlich sein kann
gefedert

hmm ich würde sagen es ist kein Integrationstest auf dieser Ebene. Wenn Sie möchten, können Sie die Rohdaten ausgeben und das Schreib-auf-Disc-Bit einfügen, wenn Sie mehr "Einheit" wünschen.
Ewan
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.