Güter arbeiten, zunächst einmal, ich fühle es notwendig ist, darauf hinzuweisen, dass im ursprünglichen Code in Frage, es gibt tatsächlich zwei Dinge, die in Angriff genommen werden müssen:
raw_input
(ein Eingang Nebeneffekt) muss verspottet werden.
print
(ein Ausgang Nebeneffekt) muss überprüft werden.
In einer idealen Funktion zur Prüfeinheit, gäbe es keine Nebenwirkungen. Eine Funktion würde einfach getestet, indem Argumente übergeben und ihre Ausgabe überprüft wird. Aber oft wollen wir Funktionen testen, die nicht ideal sind, IE, in Funktionen wie Ihrer.
Was sollen wir tun? Nun, in Python 3.3 wurden beide Probleme, die ich oben aufgelistet habe, trivial, weil das Modul die Fähigkeit erhielt, nachzuspielen und nach Nebenwirkungen zu suchen. Aber zu Beginn des Jahres 2014 waren nur 30% der Python-Programmierer auf 3.x umgestiegen, und um der anderen 70% der Python-Programmierer, die noch 2.x verwenden, zu folgen, werde ich eine Antwort skizzieren. Bei der aktuellen Rate wird 3.x 2.x nicht vor ~ 2019 überholen und 2.x wird nicht vor ~ 2027 verschwinden. Ich denke, diese Antwort wird noch einige Jahre nützlich sein.
Ich möchte die Probleme über einer nach dem anderen aufgeführten Adresse, also werde ich zunächst Ihre Funktion ändern verwenden print
als Ausgabe return
zu verwenden. Keine Überraschungen, hier ist der Code:
def answerReturn():
ans = raw_input('enter yes or no')
if ans == 'yes':
return 'you entered yes'
if ans == 'no':
return 'you entered no'
Also alles, was wir mock raw_input
tun müssen. Einfach genug - Omid Raha's answer to this very question zeigt uns, wie man das macht, indem man die __builtins__.raw_input
Implementierung mit unserer Scheinimplementierung austauscht. Außer seiner Antwort war nicht richtig in eine TestCase
organisiert und funktioniert, also werde ich das demonstrieren.
import unittest
class TestAnswerReturn(unittest.TestCase):
def testYes(self):
original_raw_input = __builtins__.raw_input
__builtins__.raw_input = lambda _: 'yes'
self.assertEqual(answerReturn(), 'you entered yes')
__builtins__.raw_input = original_raw_input
def testNo(self):
original_raw_input = __builtins__.raw_input
__builtins__.raw_input = lambda _: 'no'
self.assertEqual(answerReturn(), 'you entered no')
__builtins__.raw_input = original_raw_input
Kleine Anmerkung nur auf Python Namenskonventionen - Variablen, die vom Parser erforderlich sind, aber nicht verwendet werden _
typischerweise genannt, wie im Fall der nicht verwendeten Variable Lambda (die in der Regel ist die Aufforderung an den Benutzer gezeigt in der Fall der raw_input
, falls Sie sich wundern, warum es in diesem Fall überhaupt erforderlich ist).
Wie auch immer, das ist unordentlich und redundant. Also werde ich mit der Wiederholung durch Hinzufügen einer contextmanager
, die für einfache with
Aussagen ermöglichen, wegräumen.
from contextlib import contextmanager
@contextmanager
def mockRawInput(mock):
original_raw_input = __builtins__.raw_input
__builtins__.raw_input = lambda _: mock
yield
__builtins__.raw_input = original_raw_input
class TestAnswerReturn(unittest.TestCase):
def testYes(self):
with mockRawInput('yes'):
self.assertEqual(answerReturn(), 'you entered yes')
def testNo(self):
with mockRawInput('no'):
self.assertEqual(answerReturn(), 'you entered no')
denke ich, dass schön, den ersten Teil dieser Antworten. Weiter zum zweiten Teil - Überprüfung print
.Ich fand das viel schwieriger - ich würde gerne hören, wenn jemand eine bessere Antwort hat.
Anyways, die print
Anweisung kann nicht außer Kraft gesetzt werden, aber wenn Sie print()
Funktionen verwenden, anstatt (wie Sie sollte) und from __future__ import print_function
können Sie die folgende verwenden:
class PromiseString(str):
def set(self, newString):
self.innerString = newString
def __eq__(self, other):
return self.innerString == other
@contextmanager
def getPrint():
promise = PromiseString()
original_print = __builtin__.print
__builtin__.print = lambda message: promise.set(message)
yield promise
__builtin__.print = original_print
class TestAnswer(unittest.TestCase):
def testYes(self):
with mockRawInput('yes'), getPrint() as response:
answer()
self.assertEqual(response, 'you entered yes')
def testNo(self):
with mockRawInput('no'), getPrint() as response:
answer()
self.assertEqual(response, 'you entered no')
Der schwierige Bit ist, dass Sie brauchen zu yield
eine Antwort vor dem with
Block eingegeben wird. Aber Sie können nicht wissen, was diese Antwort sein wird, bis der print()
innerhalb des with
Blockes aufgerufen wird. Dies wäre in Ordnung, wenn Strings änderbar wären, aber das sind sie nicht. Also wurde stattdessen eine kleine Versprechen oder Proxy-Klasse gemacht - PromiseString
. Es macht nur zwei Dinge - erlauben Sie eine Zeichenfolge (oder irgendetwas, wirklich) zu setzen und lassen Sie uns wissen, ob es gleich einer anderen Zeichenfolge ist. A PromiseString
wird yield
ed und dann auf den Wert gesetzt, der normalerweise print
innerhalb des with
Blocks wäre.
Hoffentlich schätze ich all diese Tricks, die ich geschrieben habe, da ich heute Abend etwa 90 Minuten brauchte. Ich habe den ganzen Code getestet und verifiziert, dass alles mit Python 2.7 funktioniert hat.
möglich Duplikat [Versorgungseingänge zu Python Unittests] (http://stackoverflow.com/questions/2617057/supply -inputs-to-python-unittests – jonrsharpe
Ich kann die Antwort nicht finden – user3156971
Eine der drei Antworten ist * wörtlich * über die Verwendung von 'Mock' zu testen' raw_input' – jonrsharpe