python - unittest输出 - 使用典型的测试目录结构运行unittest




unittest loadtestsfromnames (10)

甚至一个简单的Python模块的通用目录结构似乎是将单元测试分离到他们自己的test目录中:

new_project/
    antigravity/
        antigravity.py
    test/
        test_antigravity.py
    setup.py
    etc.

例如看到这个Python项目howto

我的问题只是实际运行测试的常用方法什么? 我怀疑这对除我以外的所有人都是显而易见的,但是您不能仅仅从测试目录运行python test_antigravity.py ,因为import antigravity python test_antigravity.py将失败,因为模块不在路径上。

我知道我可以修改PYTHONPATH和其他搜索路径相关的技巧,但我不能相信这是最简单的方法 - 如果你是开发者,这很好,但如果他们只想检查测试,那么期望用户使用它就不现实了。通过。

另一种方法是将测试文件复制到另一个目录中,但它看起来有点笨拙,并且错过了将它们放在单独的目录中以开始。

所以,如果你刚刚将源代码下载到我的新项目中,你将如何运行单元测试? 我更喜欢一个答案,让我对我的用户说:“运行单元测试做X.”


实际运行测试的常用方法是什么?

我使用Python 3.6.2

cd new_project

pytest test/test_antigravity.py

要安装pytestsudo pip install pytest

我没有设置任何路径变量,并且我的导入不会因相同的“测试”项目结构而失败。

我评论了这个东西: if __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()

Python单元测试模块的解决方案/示例

鉴于以下项目结构:

ProjectName
 ├── project_name
 |    ├── models
 |    |    └── thing_1.py
 |    └── __main__.py
 └── test
      ├── models
      |    └── test_thing_1.py
      └── __main__.py

您可以使用python project_name从根目录运行项目,该ProjectName/project_name/__main__.py调用ProjectName/project_name/__main__.py

要使用python test运行python test ,有效地运行ProjectName/test/__main__.py ,您需要执行以下操作:

1)通过添加一个__init__.py文件,将您的test/models目录变成一个包。 这使得子目录内的测试用例可以从父test目录访问。

# ProjectName/test/models/__init__.py

from .test_thing_1 import Thing1TestCase        

2)test/__main__.py修改您的系统路径以包含project_name目录。

# ProjectName/test/__main__.py

import sys
import unittest

sys.path.append('../project_name')

loader = unittest.TestLoader()
testSuite = loader.discover('test')
testRunner = unittest.TextTestRunner(verbosity=2)
testRunner.run(testSuite)

现在,您可以在测试中成功导入project_name中的内容。

# ProjectName/test/models/test_thing_1.py    

import unittest
from project_name.models import Thing1  # this doesn't work without 'sys.path.append' per step 2 above

class Thing1TestCase(unittest.TestCase):

    def test_thing_1_init(self):
        thing_id = 'ABC'
        thing1 = Thing1(thing_id)
        self.assertEqual(thing_id, thing.id)

以下是我的项目结构:

ProjectFolder:
 - project:
     - __init__.py
     - item.py
 - tests:
     - test_item.py

我发现最好在setUp()方法中导入:

import unittest
import sys    

class ItemTest(unittest.TestCase):

    def setUp(self):
        sys.path.insert(0, "../project")
        from project import item
        # further setup using this import

    def test_item_props(self):
        # do my assertions

if __name__ == "__main__":
    unittest.main()

你应该真的使用点子工具。

使用pip install -e以开发模式安装软件包。 这是一个非常好的做法。

在下面给出的Ref url中,给出了2个经典项目(包含测试)布局,您可以按照其中任何一个。

Ref


可以使用运行选定或所有测试的包装器。

例如:

./run_tests antigravity/*.py

或者运行所有测试递归使用globbingtests/**/*.py shopt -s globstar )(由shopt -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)

查看this例子获取更多细节。

另请参阅: 如何在目录中运行所有Python单元测试?


如果你运行“python setup.py develop”,那么软件包将会在路径中。 但是你可能不想这样做,因为你可能感染你的系统python安装,这就是为什么像virtualenvbuildout这样的工具存在。


对于您的用户来说,最简单的解决方案是提供一个可执行脚本( runtests.py或其他一些),用于引导必要的测试环境,包括(如果需要)临时将根项目目录添加到sys.path中。 这不需要用户设置环境变量,像这样的东西可以在引导脚本中正常工作:

import sys, os

sys.path.insert(0, os.path.dirname(__file__))

然后,您的用户指令可以像“python runtests.py”一样简单。

当然,如果你真正需要的路径是os.path.dirname(__file__) ,那么你根本不需要将它添加到sys.path ; Python总是将当前正在运行的脚本的目录放在sys.path的开头,因此根据目录结构的不同,只需在适当的地方找到runtests.py就可以了。

此外, Python 2.7+中unittest模块 (针对Python 2.6及更早版本,它作为unittest2 反向移植)现在具有内置的测试发现功能,因此如果您希望自动发现测试,则不再需要鼻子:您的用户指令可以像“python -m unittest discover”。


我有一个单独的单元测试文件夹相同的问题。 从提到的建议中,我将绝对源路径添加到sys.path

以下解决方案的好处是,可以运行文件test/test_yourmodule.py而不必首先更改到test-directory:

import sys, os
testdir = os.path.dirname(__file__)
srcdir = '../src/projekt/dir'
sys.path.insert(0, os.path.abspath(os.path.join(testdir, srcdir)))

import yourmodule
import unittest

我认为最好的解决方案是使用unittest 命令行界面 ,该界面将目录添加到sys.path因此您不必(在TestLoader类中完成)。

例如,对于像这样的目录结构:

new_project
├── antigravity.py
└── test_antigravity.py

你可以运行:

$ cd new_project
$ python -m unittest test_antigravity

对于像你这样的目录结构:

new_project
├── antigravity
│   ├── __init__.py         # make it a package
│   └── antigravity.py
└── test
    ├── __init__.py         # also make test a package
    └── test_antigravity.py

test包内的测试模块中,您可以照常导入antigravity包及其模块:

# import the package
import antigravity

# import the antigravity module
from antigravity import antigravity

# or an object inside the antigravity module
from antigravity.antigravity import my_object

运行一个测试模块:

要运行一个测试模块,在这种情况下是test_antigravity.py

$ cd new_project
$ python -m unittest test.test_antigravity

只需引用测试模块的方式与导入它的方式相同。

运行单个测试用例或测试方法:

您也可以运行单个TestCase或单个测试方法:

$ python -m unittest test.test_antigravity.GravityTestCase
$ python -m unittest test.test_antigravity.GravityTestCase.test_method

运行所有测试:

你也可以使用测试发现来发现和运行你所有的测试,它们必须是名为test*.py (可以用-p, --pattern标志改变)的模块或者包:

$ cd new_project
$ python -m unittest discover

这将运行test包内的所有test*.py模块。


我通常在加载我的“所有测试”套件的项目目录(源目录和test共同的目录)中创建一个“运行测试”脚本。 这通常是样板代码,所以我可以在项目中重复使用它。

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

有了这个设置,你的确include antigravity在你的测试模块中include antigravity 。 缺点是你需要更多的支持代码来执行特定的测试......我只是每次都运行它们。





unit-testing