2016-08-09 4 views
4

Ich versuche, ein Python-Skript (2.7) zu testen, wo ich mit der Standardeingabe arbeite (readed mit raw_input() und writed mit einem einfachen Druck), aber ich finde nicht, wie dies und ich bin mir sicher, dass dies Problem ist sehr einfach.Wie kann ich die Standardeingabe und die Standardausgabe in Python Script mit einem Unittest testen?

Dies ist ein sehr sehr sehr Lebenslauf Code meines Skript:

def example(): 
    number = raw_input() 
    print number 

if __name__ == '__main__': 
    example() 

ich einen Unittest Test schreiben wollen, dies zu überprüfen, aber ich finde nicht, wie. Ich habe mit StringIO und anderen Dingen versucht, aber ich finde die Lösung, um das zu tun wirklich nicht einfach.

Jemand hat eine Idee?

PD: Natürlich im realen Skript verwende ich Datenblöcke mit mehreren Zeilen und andere Art von Daten.

Vielen Dank.

EDIT:

Vielen Dank für die erste wirklich konkrete Antwort, es funktioniert perfekt, nur habe ich ein kleines Problem hatte StringIO importieren, weil ich Import StringIO tat, und ich brauchte wie zu importieren from StringIO import StringIO (Ich verstehe nicht wirklich warum), aber so wie es sein mag, es funktioniert.

Aber ich habe ich ein anderes Problem mit dieser Art und Weise gefunden, in meinem Projekt muß ich mit dieser Art und Weise ein Skripte testen (die Arbeit perfekt dank Ihrer Unterstützung), aber ich möchte dies tun: Ich habe eine Datei mit vielen des Tests, um ein Skript zu übergeben, also öffne ich die Datei und lese Blöcke von Informationen mit ihren Ergebnisblöcken und ich würde gerne tun, dass der Code in der Lage sein wird, einen Block zu verarbeiten, der ihr Ergebnis überprüft und dasselbe mit anderen und anderen tut. ..

Etwas wie folgt aus:

class Test(unittest.TestCase): 
    ... 
    #open file and process saving data like datablocks and results 
    ... 
    allTest = True 
    for test in tests: 
     stub_stdin(self, test.dataBlock) 
     stub_stdouts(self) 
     runScrip() 
     if sys.stdout.getvalue() != test.expectResult: 
      allTest = False 

    self.assertEqual(allTest, True) 

ich weiß, dass vielleicht Unittest keinen Sinn jetzt nicht hat, aber man kann ein tun Idee über Ich will. Also, dieser Weg scheitert und ich weiß nicht warum.

Antwort

4

Typische Techniken beinhalten die Standard sys.stdin und sys.stdout mit Ihren gewünschten Artikeln zu verspotten. Wenn Sie nicht auf Python 3-Kompatibilität achten, können Sie einfach das Modul StringIO verwenden. Wenn Sie jedoch vorausschauend denken und sich auf Python 2.7 und 3.3+ beschränken möchten, ist das Unterstützen von Python 2 und 3 auf diese Weise ohne weiteres möglich viel Arbeit durch das io Modul (erfordert aber ein wenig Modifikation, aber diesen Gedanken in den Halt).

Angenommen, Sie haben bereits eine unittest.TestCase gehen, können Sie eine Dienstprogrammfunktion (oder Methode in der gleichen Klasse) erstellen, die sys.stdin/sys.stdout wie beschrieben ersetzt.Zunächst werden die Importe:

import sys 
import io 
import unittest 

In einem meiner letzten Projekte habe ich dies getan, für stdin, wo es eine str für die Eingänge, die ein Benutzer (oder ein anderes Programm durch Rohre) in Ihnen als stdin geben wird:

def stub_stdin(testcase_inst, inputs): 
    stdin = sys.stdin 

    def cleanup(): 
     sys.stdin = stdin 

    testcase_inst.addCleanup(cleanup) 
    sys.stdin = StringIO(inputs) 

Wie für stdout und stderr:

def stub_stdouts(testcase_inst): 
    stderr = sys.stderr 
    stdout = sys.stdout 

    def cleanup(): 
     sys.stderr = stderr 
     sys.stdout = stdout 

    testcase_inst.addCleanup(cleanup) 
    sys.stderr = StringIO() 
    sys.stdout = StringIO() 

Beachten sie, dass in beiden Fällen eine Testfall-Instanz akzeptiert, und nennt seine addCleanup Methode tha t fügt den Funktionsaufruf cleanup hinzu, der sie zurücksetzt, wo sie waren, wenn die Dauer einer Testmethode abgeschlossen ist. Dies hat zur Folge, dass sys.stdout und Freunde durch die io.StringIO Version für die Dauer von dem Zeitpunkt, als dies im Testfall aufgerufen wurde, bis zum Ende ersetzt wurden, was bedeutet, dass Sie den Wert einfach überprüfen können und sich keine Sorgen machen müssen hinter.

Besser, um dies als ein Beispiel zu zeigen. Um dies zu nutzen, können Sie einfach einen Testfall erstellen wie folgt:

class ExampleTestCase(unittest.TestCase): 

    def test_example(self): 
     stub_stdin(self, '42') 
     stub_stdouts(self) 
     example() 
     self.assertEqual(sys.stdout.getvalue(), '42\n') 

nun in Python 2, dieser Test wird nur passieren, wenn die StringIO Klasse aus dem StringIO Modul ist, und in Python 3 kein solches Modul vorhanden . Was Sie tun können, ist die Version aus dem io Modul mit einer Modifikation, die es etwas milder macht, was die Eingabe anbelangt, so dass die Unicode-Encodierung/Decodierung automatisch erfolgt, anstatt eine Ausnahme auszulösen (wie print Anweisungen) in Python 2 wird ohne die folgenden nicht gut funktionieren). Ich dies in der Regel tun für Quer Kompatibilität zwischen Python 2 und 3:

class StringIO(io.StringIO): 
    """ 
    A "safely" wrapped version 
    """ 

    def __init__(self, value=''): 
     value = value.encode('utf8', 'backslashreplace').decode('utf8') 
     io.StringIO.__init__(self, value) 

    def write(self, msg): 
     io.StringIO.write(self, msg.encode(
      'utf8', 'backslashreplace').decode('utf8')) 

Stecken Sie nun Ihr Beispiel Funktion und jedes Codefragment in dieser Antwort in eine Datei, erhalten Sie Ihre selbst Unittest enthalten, die sowohl in Python arbeitet 2 und 3 (obwohl Sie print als eine Funktion in Python 3 aufrufen müssen) für das Testen von stdio.

Noch ein Hinweis: Sie können die stub_ Funktionsaufrufe immer in die setUp Methode der TestCase setzen, wenn jede einzelne Testmethode das erfordert.

Natürlich, wenn Sie verschiedene Mocks verwandte Bibliotheken da draußen stdin out stdin/stdout verwenden möchten, sind Sie frei, dies zu tun, aber dieser Weg beruht auf keine externen Abhängigkeiten, wenn dies Ihr Ziel ist.


Für Ihre zweite Frage, haben Testfälle in einer bestimmten Art und Weise geschrieben werden, wo sie in einem Verfahren und nicht auf der Ebene der Klassen, Ihr ursprüngliches Beispiel fehl gekapselt werden müssen. Aber man könnte so etwas wie dies tun wollen:

class Test(unittest.TestCase): 

    def helper(self, data, answer, runner): 
     stub_stdin(self, data) 
     stub_stdouts(self) 
     runner() 
     self.assertEqual(sys.stdout.getvalue(), answer) 
     self.doCleanups() # optional, see comments below 

    def test_various_inputs(self): 
     data_and_answers = [ 
      ('hello', 'HELLOhello'), 
      ('goodbye', 'GOODBYEgoodbye'), 
     ] 

     runScript = upperlower # the function I want to test 

     for data, answer in data_and_answers: 
      self.helper(data, answer, runScript) 

Der Grund, warum Sie vielleicht doCleanups nennen, ist die Bereinigung Stapel zu verhindern, dass immer so tief wie alle data_and_answers Paare gibt es, aber das wird alles Pop-off Der Cleanup-Stack. Wenn Sie also noch andere Dinge haben, die am Ende aufgeräumt werden müssen, könnte dies problematisch sein - Sie können das dort belassen, da alle stdio-bezogenen Objekte am Ende in der gleichen Reihenfolge wiederhergestellt werden , so wird der echte immer da sein.Nun wird die Funktion wollte ich Test:

def upperlower(): 
    raw = raw_input() 
    print (raw.upper() + raw), 

Also ja, ein bisschen Erklärung für das, was ich tat, könnte helfen: innerhalb einer TestCase Klasse erinnern, verlässt sich der Rahmen streng auf die assertEqual und Freunde der Instanz für es zu funktionieren. Um sicherzustellen, dass Tests auf der richtigen Ebene durchgeführt werden, möchten Sie diese Behauptungen wirklich ständig aufrufen, damit hilfreiche Fehlermeldungen angezeigt werden, sobald der Fehler bei den Eingaben/Antworten auftritt, die nicht richtig angezeigt werden bis zum Ende wie das, was du mit der for-Schleife gemacht hast (das wird dir sagen, dass etwas nicht in Ordnung war, aber nicht genau wo von Hunderten und jetzt bist du verrückt). Auch die helper Methode - Sie können es nennen, was Sie wollen, solange es nicht mit test beginnt, weil dann das Framework versuchen wird, es als eins zu laufen, und es wird schrecklich scheitern. Also folge einfach dieser Konvention und du kannst grundsätzlich Vorlagen in deinem Testfall haben, mit denen du deinen Test durchführen kannst - du kannst es dann in einer Schleife mit einer Reihe von Inputs/Outputs verwenden, wie ich es gemacht habe.

Was Ihre andere Frage:

nur ich habe ein kleines Problem importieren StringIO habe, weil ich Import StringIO tat, und ich brauchte, wie zur Einfuhr aus StringIO Import StringIO (Ich verstehe nicht wirklich warum), aber so wie es sein mag, es funktioniert.

Nun, wenn Sie an meiner ursprünglichen Code sehe ich Sie zeigte, wie import io tat und überwog dann die io.StringIO Klasse von class StringIO(io.StringIO) definieren. Allerdings funktioniert es für Sie, weil Sie dies ausschließlich von Python 2 aus tun, während ich versuche, meine Antworten auf Python 3 so oft wie möglich anzusprechen, da Python 2 (wahrscheinlich definitiv dieses Mal) in weniger als 5 Jahren nicht unterstützt wird. Denken Sie an die zukünftigen Benutzer, die diesen Beitrag lesen könnten, der ein ähnliches Problem hatte wie Sie. Wie auch immer, ja, das Original from StringIO import StringIO funktioniert, denn das ist die StringIO Klasse aus dem StringIO Modul. Obwohl from cStringIO import StringIO sollte funktionieren, als das die C Version des Moduls StringIO importiert. Es funktioniert, weil sie alle nah genug Schnittstellen bieten, und so werden sie im Grunde wie beabsichtigt funktionieren (bis Sie natürlich versuchen, dies unter Python 3 auszuführen).

Noch einmal, alles zusammen mit meinem Code zusammen zu setzen sollte in self-contained working test script resultieren. Denken Sie daran, sich die Dokumentation anzusehen und die Form des Codes zu befolgen, und nicht Ihre eigene Syntax zu erfinden und zu hoffen, dass die Dinge funktionieren (und genau, warum Ihr Code nicht funktioniert hat, weil der "Test" -Code an der Klasse definiert wurde) wurde erstellt, also wurde all das ausgeführt, während Python das Modul importiert hat, und da keine der Dinge, die für den Test benötigt werden, überhaupt verfügbar sind (nämlich die Klasse selbst existiert noch nicht einmal), die ganze Sache stirbt nur in Anfällen von zuckender Agonie). Fragen hier zu stellen hilft auch, auch wenn das Problem, mit dem du konfrontiert wirst, etwas wirklich normales ist. Wenn du nicht einen schnellen und einfachen Namen hast, um nach deinem genauen Problem zu suchen, ist es schwierig herauszufinden, wo du schief gegangen bist? :) Wie auch immer, viel Glück und schade, dass Sie sich die Mühe gemacht haben, Ihren Code zu testen.


Es gibt andere Methoden, aber wenn man bedenkt, dass die anderen Fragen/Antworten, die ich an hier betrachtete SO nicht zu helfen scheint, hoffe ich dieses dieses.Andere, die als Referenz:

Natürlich ist es fletscht wiederholen, dass alle diese kann in Python mit unittest.mock verfügbar gemacht werden 3.3+ oder original/rolling backport version on pypi, aber wenn man bedenkt, dass Diese Bibliotheken verbergen einige der Feinheiten in dem, was tatsächlich passiert, sie können einige Details darüber verbergen, was tatsächlich passiert (oder passieren muss) oder wie die Umleitung tatsächlich geschieht. Wenn Sie möchten, können Sie auf unittest.mock.patch nachlesen und gehen Sie leicht auf die StringIO patchen sys.stdout Abschnitt.

+0

Vielen Dank für die wirklich gute Antwort @metatoaster Ich habe weitere Informationen über das Problem hinzugefügt, das ich jetzt mit Ihrer Art und Weise gefunden habe, eine for-Schleife einzulesen. Danke nochmal. –

Verwandte Themen