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.
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. –