Как определить, выполняется ли код в doctest?
Как я могу сделать так, чтобы мой код (Python 2.7) знал, выполняется ли он в тестовом режиме?
Сценарий таков: у меня есть функция, которая print()
s некоторый вывод в файловый дескриптор, переданный в качестве аргумента, что-то вроде этого:
from __future__ import print_function
def printing_func(inarg, file=sys.stdout):
# (do some stuff...)
print(result, file=file)
Но когда я пытаюсь использовать printing_func()
в тесте тест не пройден; из-за моей спецификации аргумента ключевого слова file
при вызове print()
, результат на самом деле идет к sys.stdout
а не какое-либо перенаправление вывода по умолчанию, установленное модулем doctest, и doctest никогда не увидит вывод.
Так как я могу сделать printing_func()
знать, работает ли он внутри doctest, так что он знает, не передать file
Ключевое слово аргумент при вызове print()
?
3 ответа
Я разобрался с ответом после прочтения doctest.py
; размещение здесь для потомков...
Doctest перенаправляет стандартный вывод, назначая новый файловый дескриптор sys.stdout
, Проблема заключалась в том, что описание моей функции закрывалось по сравнению со значением оригинала sys.stdout
дескриптор файла до переопределения документа.
Вместо этого, если я сделаю следующее:
def printing_func(inarg, file=None):
# (do some stuff...)
if file is None:
file = sys.stdout
print(result, file=file)
затем printing_func()
захватит sys
модуль, а не sys.stdout
и когда он запустится, он получит переопределенный doctest stdout
атрибут из sys
если работает внутри теста.
РЕДАКТИРОВАТЬ: Это также дает простой способ проверить, работаем ли мы внутри doctest:
def inside_doctest(original_stdout=sys.stdout):
return original_stdout != sys.stdout
Версия Нитена inside_doctest
кажется слишком широким. Это не так уж необычно переопределить sys.stdout
либо для регистрации, либо при тестировании в среде, отличной от doctest, так что это даст ложные срабатывания.
Более узкий тест выглядит так:
import sys
def in_doctest():
##
## direct doctest
if hasattr(sys.modules['__main__'], '_SpoofOut'):
return True
##
## doctest via pytest
if sys.modules['__main__'].__dict__.get('__file__', '').endswith('/pytest'):
return True
##
return False
def test():
"""
>>> print 'inside comments, running in doctest?', in_doctest()
inside comments, running in doctest? True
"""
print 'outside comments, running in doctest?', in_doctest()
if __name__ == '__main__':
test()
in_doctest
тесты для _SpoofOut
класс doctest использует для замены sys.stdout
, Существуют и другие атрибуты модуля doctest, которые можно проверить таким же образом. Не то, чтобы вы могли помешать другому модулю повторно использовать имя, но это имя не является обычным, поэтому, вероятно, достойный тест.
Поместите вышеупомянутое в test.py. Запустив его в не-тестовом режиме, python test.py
выходы:
outside comments, running in doctest? False
Запуск в режиме многословной документации, python -m doctest test.py -v
выходы:
Trying:
print 'inside comments, running in doctest?', in_doctest()
Expecting:
inside comments, running in doctest? True
ok
Я согласен с комментариями других, что создание кода, осведомленного о doctest, вообще плохая идея. Я сделал это только в несколько экзотических обстоятельствах - когда мне нужно было создавать тестовые примеры с помощью генератора кода, потому что их было слишком много, чтобы эффективно создавать вручную. Но если вам нужно это сделать, вышесказанное является достойным тестом для этого.
FWIW (и извините за опоздание и избыточность) многие разработчики считают "если тест" как антипаттерн.
Т.е., если ваш тестируемый код делает разные вещи, когда он тестируется, чем когда он запускается "по-настоящему", вы напрашиваетесь на неприятности. Даже если вы верите, что делаете это по уважительной причине. Следовательно, комментарии выше приветствуют ваше решение, которое не делает этого. Когда я испытываю желание использовать шаблон "if test", я пытаюсь реорганизовать вещи, чтобы они не были нужны.
Я просто проверяю, загружен ли модуль 'doctest'.
def in_doctest():
import sys
return 'doctest' in sys.modules