对于一个简单的Python模块来说,非常常见的目录结构似乎是将单元测试分离到它们自己的测试目录中:
new_project/
antigravity/
antigravity.py
test/
test_antigravity.py
setup.py
etc.
我的问题很简单,实际运行测试的通常方式是什么?我怀疑这对每个人来说都是显而易见的,除了我,但你不能只是从测试目录运行python test_antigravity.py,因为它的导入antigravity将失败,因为模块不在路径上。
我知道我可以修改PYTHONPATH和其他与搜索路径相关的技巧,但我不能相信这是最简单的方法——如果您是开发人员,这很好,但如果用户只是想检查测试是否通过,那么期望他们使用这种方法是不现实的。
另一种替代方法是将测试文件复制到另一个目录中,但这似乎有点愚蠢,并且没有注意到将它们放在一个单独的目录中。
那么,如果您刚刚下载源代码到我的新项目,您将如何运行单元测试?我更喜欢这样的答案:“要运行单元测试,请执行x。”
实际运行测试的通常方式是什么
我使用的是Python 3.6.2
cd new_project
pytest test/test_antigravity.py
安装pytest: sudo pip install pytest
我没有设置任何路径变量,我的导入不会失败与相同的“测试”项目结构。
我注释掉了这些东西:如果__name__ == '__main__'像这样:
test_antigravity.py
import antigravity
class TestAntigravity(unittest.TestCase):
def test_something(self):
# ... test stuff here
# if __name__ == '__main__':
#
# if __package__ is None:
#
# import something
# sys.path.append(path.dirname(path.dirname(path.abspath(__file__))))
# from .. import antigravity
#
# else:
#
# from .. import antigravity
#
# unittest.main()
我通常在项目目录中创建一个“运行测试”脚本(对于源目录和测试都是通用的),用于加载我的“所有测试”套件。这通常是样板代码,所以我可以在项目之间重用它。
run_tests.py:
import unittest
import test.all_tests
testSuite = test.all_tests.create_test_suite()
text_runner = unittest.TextTestRunner().run(testSuite)
test/all_tests.py(来自如何在一个目录中运行所有Python单元测试?)
import glob
import unittest
def create_test_suite():
test_file_strings = glob.glob('test/test_*.py')
module_strings = ['test.'+str[5:len(str)-3] for str in test_file_strings]
suites = [unittest.defaultTestLoader.loadTestsFromName(name) \
for name in module_strings]
testSuite = unittest.TestSuite(suites)
return testSuite
有了这个设置,你确实可以在你的测试模块中包含反重力。缺点是你需要更多的支持代码来执行一个特定的测试…我每次都把它们都运行一遍。
这种方式将允许您从任何您想要的地方运行测试脚本,而不会从命令行中破坏系统变量。
这将把主项目文件夹添加到python路径中,其位置相对于脚本本身,而不是相对于当前工作目录。
import sys, os
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
将其添加到所有测试脚本的顶部。这将把主项目文件夹添加到系统路径中,因此从那里导入的任何模块现在都可以工作了。你在哪里做测试并不重要
显然,您可以更改project_path_hack文件以匹配您的主项目文件夹位置。
可以使用包装器来运行选定的或所有测试。
例如:
./run_tests antigravity/*.py
或者递归地运行所有测试使用globbing (tests/**/*.py)(通过shop -s globstar启用)。
包装器基本上可以使用argparse来解析参数,比如:
parser = argparse.ArgumentParser()
parser.add_argument('files', nargs='*')
然后加载所有测试:
for filename in args.files:
exec(open(filename).read())
然后将它们添加到你的测试套件中(使用inspect):
alltests = unittest.TestSuite()
for name, obj in inspect.getmembers(sys.modules[__name__]):
if inspect.isclass(obj) and name.startswith("FooTest"):
alltests.addTest(unittest.makeSuite(obj))
并运行它们:
result = unittest.TextTestRunner(verbosity=2).run(alltests)
查看这个示例了解更多细节。
请参见:如何在一个目录中运行所有Python单元测试?
如果您正在寻找命令行解决方案:
基于以下目录结构(使用专用源目录进行概括):
new_project/
src/
antigravity.py
test/
test_antigravity.py
Windows:(在new_project中)
$ set PYTHONPATH=%PYTHONPATH%;%cd%\src
$ python -m unittest discover -s test
如果您想在批for循环中使用此语句,请参阅此问题。
Linux:(在new_project中)
$ export PYTHONPATH=$PYTHONPATH:$(pwd)/src [I think - please edit this answer if you are a Linux user and you know this]
$ python -m unittest discover -s test
使用这种方法,还可以在必要时向PYTHONPATH添加更多目录。