由于您是在模块级别执行此操作,我假设您也有 packaged your modules as a proper Python package 使用类似 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,以及一个包含 test_suite 设置的 __init__.py,如下所示
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)
最后,由于这是一个真正的测试套件,由mymodules.tests 中的make_suite 函数生成,因此必须使用测试运行器来执行该测试套件,幸运的是,它包含在默认单元测试中框架作为一个简单的命令,可以这样完成:
$ python -m unittest mymodule.tests.make_suite
.
----------------------------------------------------------------------
Ran 1 test in 0.014s
OK
此外,由于这是一个真正的测试套件,它可以与来自 unittest 模块的测试套件 globbing 集成,以将所有内容组合成一个完整的测试套件,用于您的整个包。
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 命令可用于执行完整测试套件返回的测试。