2014-07-22 12 views
9

Ich werde das vorausschicken, indem ich zugebe, dass ich wahrscheinlich etwas mache, was ich nicht tun sollte. Aber da ich schon so tief bin, kann ich auch verstehen, warum so etwas passiert.Muting stdout und stderr während Mokka-Tests

Ich verwende Mocha, um einige Node.js Code zu testen. Dieser Code verwendet die Winston-Protokollierungsbibliothek, die process.stdout.write() und process.stderr.write() (source) direkt aufruft. Es läuft gut; Ich habe keine Beschwerden über dieses Verhalten.

Allerdings, wenn ich Unit-Test dieser Code wird der Ausgang des Mokka-Test Läufer gelegentlich mit den Linien der Protokollausgabe durchsetzt, die in einigen Reportern hässlich ist (dot, bdd) und geradezu ungültig in anderen (xunit). Ich wollte diese Ausgabe blockieren, ohne Winston zu modifizieren oder zu unterklassifizieren, und ich wollte vermeiden, die Anwendung selbst zu modifizieren, wenn ich sie vermeiden könnte.

Was bei meiner Ankunft eine Reihe von Utility-Funktionen war, die vorübergehend die Knoten builtins mit einer no-op-Funktion ersetzen kann, und umgekehrt:

var stdout_write = process.stdout._write, 
    stderr_write = process.stderr._write; 

function mute() { 
    process.stderr._write = process.stdout._write = function(chunk, encoding, callback) { 
     callback(); 
    }; 
} 

function unmute() { 
    process.stdout._write = stdout_write; 
    process.stderr._write = stderr_write; 
} 

Innerhalb der verschiedenen Testspezifikt, rief ich mute() direkt vor jeder Anruf oder jede Behauptung, die unerwünschte Ausgabe erzeugte, und unmute() direkt danach. Es fühlte sich ein wenig hacky an, aber es funktionierte - kein einziges Byte unerwünschter Ausgabe erschien auf der Konsole, wenn die Tests ausgeführt wurden.

Jetzt wird es komisch!

Zum ersten Mal habe ich versucht, die Ausgabe in eine Datei umleiten:

mocha spec_file.js > output.txt 

Die unerwünschte Ausgabe kam zurück! Jede Ausgabe, die an stdout gesendet wurde, wird in der Datei angezeigt. Hinzufügen 2>&1, bekomme ich stderr in der Datei auch. In beiden Fällen erscheint jedoch nichts auf der Konsole.

Warum verhält sich der Testcode so unterschiedlich zwischen den beiden Aufrufen? Meine Vermutung ist, dass Mocha eine Art Test durchführt, um zu bestimmen, ob er in ein TTY schreibt oder nicht, aber ich konnte keinen offensichtlichen Ort erkennen, an dem er das Verhalten seiner Schreibvorgänge ändert.

Auch die weitergehende Frage, gibt es einen richtig Weg stdout/stderr während der Tests stumm zu schalten, ohne all potenziell Logging App-Code in einem bedingten, dass die Kontrollen für die Testumgebung Verpackung?

+0

Sie wissen nicht, Winston, ist aber möglich, dass er prüft, ob stdout und stderr ein tty sind, und wenn ja anders verhält (wie: in den Farben der Anmeldung usw.)? Wenn ja, überschreibe ich nicht 'write' und' _write', sondern erzeuge einen "null" -Stream und ersetze 'stdout' und' stderr' komplett. Ich wäre auch überrascht, wenn es für Winston keine "globale Einstellung" gäbe, die genau das tun würde: stumm. (Sagen wir: Log Level auf Minimum reduzieren). –

Antwort

2

Ich entdeckte eine wahrscheinliche Ursache für dieses Verhalten. Es hat tatsächlich damit zu tun, ob stdout/stderr ein TTY ist oder nicht.

Wenn das Skript in einer Konsole ausgeführt wird, sind diese beiden TTYs und process.stdout und process.stderr erscheinen Instanzen tty.WriteStream und nicht zu sein, wie ich ursprünglich angenommen, ein stream.Writable. Soweit meine Interaktionen gingen, waren die beiden Klassen wirklich nicht so unterschiedlich - beide hatten öffentliche write() Methoden, die interne _write() Methoden anriefen, und beide teilten die gleichen Methodensignaturen.

Beim Pipettieren in eine Datei wurden die Dinge ein wenig anders. process.stdout und process.stderr waren Instanzen einer anderen Klasse, die nicht sofort vertraut war. Ich kann mir vorstellen, es ist ein fs. SyncWriteStream, aber das ist ein Stich im Dunkeln. Wie auch immer, diese Klasse hat keine _write() Methode, also war es sinnlos, sie zu überschreiben.

Die Lösung war, eine Ebene höher zu bewegen und mein Muting mit write() statt _write() zu tun. Es macht das gleiche, und es tut es konsequent unabhängig davon, wohin die Ausgabe geht.

5

Siehe https://www.npmjs.org/package/mute

it('should shut the heck up', function (done) { 
    var unmute = mute() 
    app.options.defaults = true; 

    app.run(function() { 
     unmute(); 

     helpers.assertFiles([ 
      ['package.json', /"name": "temp-directory"/], 
      ['README.md', /# TEMP.Directory/] 
     ]); 

     done(); 
    }); 
});