Поскольку вы делаете это на уровне модуля, я предполагаю, что вы также упаковали свои модули как правильный пакет Python, используя что-то вроде setuptools
, и быть развернутым в некоторой среде, где затем будут выполняться тесты. Кроме того, вы лишь частично правы в предположении, что __file__
не существует - он также не определен для модуля, импортированного из заархивированного яйца Python (которые становятся все более редкими, поскольку колеса стали де-факто методом упаковки, но они могут и существуют).
Существует ряд возможных подходов, различающихся по сложности и компромиссам, и их работа зависит от того, как структурированы тестируемые модули.
1) (Не рекомендуется, но все равно включен, потому что иногда это работает лучше всего в случаях простейших примеров.) Самый ленивый, но самый стабильный, автономный и кроссплатформенный способ - и он предполагает, что методы открытия файлов выполняются только в тот модуль, который должен был быть протестирован и выполнен с использованием одного и того же вызова (например, open
), использование extraglobs
можно использовать для замены вызова open
. например
from io import StringIO
import doctest
import mymodule
files = {
'path/to/file1.txt': '... contents of file1.txt ...',
'path/to/file2.txt': '... contents of file2.txt ...',
}
def lazyopen(p, flag='r'):
result = StringIO(files[p] if flag == 'r' else '')
result.name = p
return result
doctest.testmod(mymodule, extraglobs={'open': lazyopen})
2) Создать настоящий набор тестов, а не использовать встроенные через doctest.testmod
Хотя сокращение полезно, оно слишком ограничено, поскольку является автономным, его нельзя использовать в сочетании с другими наборами тестов, которые могут быть созданы. Рассмотрите возможность создания специального тестового модуля (например, mymodule/tests.py
). Обычно я предпочитаю создавать каталог с именем mymodule/tests
, с юнит-тестами с именем что-то вроде test_mysubmodule.py
и __init__.py
, который содержит настройку test_suite, например так
def make_suite():
import mymodule
import os
def setUp(suite):
suite.olddir = os.getcwd() # save the current working directory
os.chdir(targetdir) # need to define targetdir
def tearDown(suite):
os.chdir(suite.olddir) # restore the original working directory
return doctest.DocTestSuite(mymodule, setUp=setUp, tearDown=tearDown)
Итак, мы рассмотрели основы, но нужно определить targetdir
. Опять же, несколько вещей, которые вы можете рассмотреть:
1) Создайте временный каталог и заполните каталог необходимыми файлами, используя setup
и os.chdir
к нему, и удалите временный каталог в tearDown
. Либо вручную записать данные, хранящиеся в виде строк внутри тестового модуля, либо скопировать из своего проекта, либо извлечь из архива, но как мы их получим? Что приводит к...
2) Если исходные файлы находятся внутри вашего проекта и setuptools
доступны/установлены в среде, просто используйте pkg_resources.resource_filename
, чтобы получить местоположение, и присвойте ему targetdir
. setUp
теперь может выглядеть примерно так
def setUp(suite):
suite.olddir = os.getcwd()
targetdir = pkg_resources.resource_filename('mymodule', '')
os.chdir(targetdir)
Кроме того, наконец, поскольку теперь это настоящий набор тестов, созданный функцией make_suite
внутри mymodules.tests
, его выполнение должно выполняться с помощью средства запуска тестов, которое, к счастью, включено в стандартную структуру юнит-тестов в виде простой команды, которая может быть сделано так:
$ python -m unittest mymodule.tests.make_suite
.
----------------------------------------------------------------------
Ran 1 test in 0.014s
OK
Кроме того, поскольку это настоящий набор тестов, его можно интегрировать с набором тестов из модуля unittest
, чтобы объединить все в один полный набор тестов для всего вашего пакета.
def make_suite():
# ... the other setup code
# this loads all unittests in mymodule from `test_*.py` files
# inside `mymodule.tests`
test_suite = test_loader.discover(
'mymodule.tests', pattern='test_*.py')
test_suite.addTest(
doctest.DocTestSuite(mymodule, setUp=setUp, tearDown=tearDown))
return test_suite
Опять же, команда python -m unittest
может использоваться для выполнения тестов, возвращенных полным набором тестов.
person
metatoaster
schedule
18.11.2017