Ладно, во-первых, я считаю, что необходимо отметить, что в исходном коде в вопросе, есть две вещи, которые необходимо решить:
raw_input
(входной побочный эффект) необходимо высмеять.
print
(выходной побочный эффект) необходимо проверить.
В идеальной функции для модульного тестирования побочных эффектов не было. Функция будет просто проверена путем передачи аргументов, и ее выход будет проверен. Но часто мы хотим проверить функции, которые не идеальны, IE, в таких функциях, как ваша.
Так что нам делать? Что ж, в Python 3.3 обе перечисленные выше проблемы стали тривиальными, потому что модуль unittest
получил возможность издеваться и проверять наличие побочных эффектов. Но с начала 2014 года только 30% программистов на Python перешли на 3.x, поэтому для других 70% программистов на Python, все еще использующих 2.x, я опишу ответ. При текущей скорости 3.x не догонит 2.x до ~ 2019, а 2.x не исчезнет до ~ 2027. Поэтому я считаю, что этот ответ будет полезен уже несколько лет.
Я хочу решить проблемы, перечисленные выше по одному, поэтому я собираюсь изначально изменить свою функцию с помощью print
в качестве вывода на использование return
. Никаких сюрпризов здесь нет, что код:
def answerReturn():
ans = raw_input('enter yes or no')
if ans == 'yes':
return 'you entered yes'
if ans == 'no':
return 'you entered no'
Так все, что нужно сделать, это насмешка raw_input
. Достаточно легко - Omid Raha's answer to this very question показывает нам, как это сделать, выполняя реализацию __builtins__.raw_input
с нашей макетной реализацией. За исключением того, что его ответ был неправильно организован в TestCase
и функции, я продемонстрирую это.
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
Небольшое замечание только на Python именования - переменные, которые требуются анализатором, но не используются, как правило, с именем _
, как и в случае неиспользуемой переменной лямбда (которые в обычных условиях подсказка показывается пользователю в случай raw_input
, если вам интересно, почему это вообще необходимо в этом случае).
В любом случае, это грязно и избыточно. Поэтому я собираюсь покончить с повторением, добавив в contextmanager
, что позволит использовать простые операторы with
.
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')
Я думаю, что это хорошо отвечает на первую часть этого вопроса. На вторую часть - проверку print
.Я нашел это намного сложнее - я бы хотел услышать, есть ли у кого-то лучший ответ.
В любом случае, print
утверждение не может быть отменено, но если вы используете print()
функции вместо (как следует) и from __future__ import print_function
вы можете использовать следующее:
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')
Хитрая немного здесь является то, что вам нужно до yield
ответ на ввод блока with
. Но вы не можете знать, какой ответ будет до тех пор, пока не вызывается print()
внутри блока with
. Это было бы прекрасно, если строки были изменчивыми, но это не так. Поэтому вместо этого было сделано небольшое обещание или класс прокси - PromiseString
. Он делает только две вещи: позволяет установить строку (или что-нибудь, действительно) и сообщить нам, если она равна другой строке. A PromiseString
: yield
ed, а затем устанавливается значение, которое обычно равно print
в блоке with
.
Надеюсь, вы оцените все эти обманки, которые я написал, так как мне потребовалось около 90 минут, чтобы собраться сегодня вечером. Я тестировал весь этот код и проверял, что все это работает с Python 2.7.
возможно дубликат [входы питания на питон UnitTests] (http://stackoverflow.com/questions/2617057/supply -inputs-to-python-unittests) – jonrsharpe
Я не могу найти ответ там – user3156971
Один из трех ответов * буквально * об использовании 'mock' для проверки' raw_input' – jonrsharpe