运行具有典型测试目录结构的unittest

时间:2009-12-13 16:10:24

标签: python unit-testing

即使是简单的Python模块,非常常见的目录结构似乎是将单元测试分成他们自己的test目录:

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

例如,请参阅此Python project howto

我的问题只是实际运行测试的常用方法是什么?我怀疑除了我以外,每个人都很明显,但你不能只从测试目录中运行python test_antigravity.py因为import antigravity将失败,因为模块不在路径上。

我知道我可以修改PYTHONPATH和其他搜索路径相关的技巧,但我不能相信这是最简单的方法 - 如果你是开发人员就没关系,但如果他们只是想检查你的用户就不太现实测试正在通过。

另一种选择只是将测试文件复制到另一个目录中,但它看起来有点愚蠢,并且错过了将它们放在一个单独的目录中的重点。

那么,如果您刚刚将源代码下载到我的新项目中,您将如何运行单元测试?我更喜欢一个答案,让我告诉我的用户:“要运行单元测试做X.”

23 个答案:

答案 0 :(得分:552)

我认为最好的解决方案是使用unittest command line interface,这会将目录添加到sys.path,因此您不必({1}完成} class)。

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

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

运行所有测试:

您还可以使用test discovery来发现并运行所有测试,它们必须是名为$ python -m unittest test.test_antigravity.GravityTestCase $ python -m unittest test.test_antigravity.GravityTestCase.test_method 的模块或包(可以使用test*.py标志进行更改):< / p>

-p, --pattern

这将运行$ cd new_project $ python -m unittest discover 包中的所有test*.py模块。

答案 1 :(得分:46)

对您的用户来说,最简单的解决方案是提供一个可执行脚本(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放在正确的位置即可。

此外,unittest module in Python 2.7+(对于Python 2.6及更早版本,已移植为unittest2)现在内置了test discovery,因此如果您想要自动化测试发现,则不再需要使用鼻子:您的用户说明可以像“python -m unittest discover”一样简单。

答案 2 :(得分:20)

我通常在项目目录(源目录和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(来自How do I run all Python unit tests in a directory?

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。缺点是你需要更多的支持代码才能执行特定的测试...我只是每次都运行它们。

答案 3 :(得分:18)

从您链接的文章:

  

创建一个test_modulename.py文件   把你的单元测试放进去吧。以来   测试模块是单独的   您可能需要的代码中的目录   添加模块的父目录   到你的PYTHONPATH跑步   它们:

$ cd /path/to/googlemaps

$ export PYTHONPATH=$PYTHONPATH:/path/to/googlemaps/googlemaps

$ python test/test_googlemaps.py
     

最后,还有一个更受欢迎   Python的单元测试框架   (这很重要!),鼻子。鼻子   有助于简化和扩展内置   unittest框架(它可以,为   例如,自动找到您的测试   代码和设置你的PYTHONPATH   你),但它不包括在内   标准的Python发行版。

也许您应该按照它的建议来看nose

答案 4 :(得分:8)

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

答案 5 :(得分:8)

我有同样的问题,有一个单独的单元测试文件夹。根据上述建议,我将绝对源路径添加到sys.path

以下解决方案的好处是,可以运行文件test/test_yourmodule.py而无需先更改为测试目录:

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

import antigravity
import unittest

答案 6 :(得分:8)

我遇到相同的问题已有很长时间了。我最近选择的是以下目录结构:

project_path
├── Makefile
├── src
│   ├── script_1.py
│   ├── script_2.py
│   └── script_3.py
└── tests
    ├── __init__.py
    ├── test_script_1.py
    ├── test_script_2.py
    └── test_script_3.py

在测试文件夹的__init__.py脚本中,我写了以下内容:

import os
import sys
PROJECT_PATH = os.getcwd()
SOURCE_PATH = os.path.join(
    PROJECT_PATH,"src"
)
sys.path.append(SOURCE_PATH)

对于共享项目而言,超级重要的是Makefile,因为它强制正确运行脚本。这是我放入Makefile中的命令:

run_tests:
    python -m unittest discover .

Makefile之所以重要,不仅是因为它运行的命令,还因为它是从运行它的地方。如果您要在测试中进行cd并执行python -m unittest discover .,它将无法正常工作,因为unit_tests中的 init 脚本会调用os.getcwd(),这将指向错误的绝对路径(即将被附加到sys.path,并且您将缺少源文件夹)。自发现发现所有测试以来,脚本便会运行,但它们无法正常运行。因此,Makefile可以避免记住此问题。

我真的很喜欢这种方法,因为我不必触摸src文件夹,单元测试或环境变量,并且一切运行都非常顺利。

让我知道你们是否喜欢它。

希望有帮助,

答案 7 :(得分:5)

如果你使用VS Code并且你的测试与你的项目位于同一级别,那么运行和调试你的代码并不是开箱即用的。你可以做的是改变你的launch.json文件:

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Python",
            "type": "python",
            "request": "launch",
            "stopOnEntry": false,
            "pythonPath": "${config:python.pythonPath}",
            "program": "${file}",
            "cwd": "${workspaceRoot}",
            "env": {},
            "envFile": "${workspaceRoot}/.env",
            "debugOptions": [
                "WaitOnAbnormalExit",
                "WaitOnNormalExit",
                "RedirectOutput"
            ]
        }    
    ]
}

这里的关键是envFile

"envFile": "${workspaceRoot}/.env",

在项目的根目录中添加.env文件

在.env文件中添加项目根目录的路径。这将暂时添加

  

PYTHONPATH = C:\ YOUR \ PYTHON \ PROJECT \ ROOT_DIRECTORY

项目的路径,您将能够使用VS Code中的调试单元测试

答案 8 :(得分:5)

Python unittest模块的解决方案/示例

鉴于以下项目结构:

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

您可以使用调用python project_name的{​​{1}}从根目录运行项目。

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

1)通过添加ProjectName/test/__main__.py文件将您的test/models目录转换为包。这使得子目录中的测试用例可以从父__init__.py目录访问。

test

2)修改# ProjectName/test/models/__init__.py from .test_thing_1 import Thing1TestCase 中的系统路径以包含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

答案 9 :(得分:5)

使用setup.py develop使您的工作目录成为已安装的Python环境的一部分,然后运行测试。

答案 10 :(得分:4)

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

例如:

./run_tests antigravity/*.py

或以递归方式运行所有测试使用globbingtests/**/*.py)(由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示例了解更多详情。

另请参阅:How to run all Python unit tests in a directory?

答案 11 :(得分:4)

我注意到如果从“src”目录运行unittest命令行界面,则导入无需修改即可正常工作。

python -m unittest discover -s ../test

如果要将其放在项目目录中的批处理文件中,可以执行以下操作:

setlocal & cd src & python -m unittest discover -s ../test

答案 12 :(得分:3)

以下是我的项目结构:

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()

答案 13 :(得分:3)

  

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

我使用Python 3.6.2

cd new_project

pytest test/test_antigravity.py

安装 pytest sudo pip install pytest

我没有设置任何路径变量,而且我的导入没有失败,同样的&#34;测试&#34;项目结构。

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

答案 14 :(得分:2)

Python 3 +

添加到@Pierre

使用unittest这样的目录结构:

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

要运行测试模块test_antigravity.py

$ cd new_project
$ python -m unittest test.test_antigravity

或一个TestCase

$ python -m unittest test.test_antigravity.GravityTestCase

必填,即使为空也不要忘记__init__.py,否则将不起作用。

答案 15 :(得分:2)

没有伏都教,您无法从父目录导入。这是至少可以在Python 3.6上使用的另一种方式。

首先,具有以下内容的文件test / context.py:

import sys
import os
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))

然后在文件test / test_antigravity.py中具有以下导入:

import unittest
try:
    import context
except ModuleNotFoundError:
    import test.context    
import antigravity

请注意,此try-except子句的原因是

    使用“ python test_antigravity.py”和运行时,
  • 导入test.context 失败 当从new_project目录中使用“ python -m unittest”运行时,
  • 导入上下文失败。

通过这种诡计,他们俩都可以工作。

现在,您可以使用以下命令在测试目录中运行所有测试文件:

$ pwd
/projects/new_project
$ python -m unittest

或使用以下命令运行单个测试文件:

$ cd test
$ python test_antigravity

好吧,比在test_antigravity.py中包含context.py的内容漂亮得多,但可能要少一些。欢迎提出建议。

答案 16 :(得分:1)

此BASH脚本将在文件系统中的任何位置执行python unittest测试目录,无论您位于哪个工作目录中。

当留在./src./example工作目录中并且需要快速进行单元测试时,这很有用:

#!/bin/bash

this_program="$0"
dirname="`dirname $this_program`"
readlink="`readlink -e $dirname`"

python -m unittest discover -s "$readlink"/test -v

在生产过程中,无需test/__init__.py文件就可以负担您的包/内存开销。

答案 17 :(得分:0)

如果测试目录中有多个目录,则必须在每个目录中添加一个__init__.py文件。

/home/johndoe/snakeoil
└── test
    ├── __init__.py        
    └── frontend
        └── __init__.py
        └── test_foo.py
    └── backend
        └── __init__.py
        └── test_bar.py

然后一次运行每个测试,运行:

python -m unittest discover -s /home/johndoe/snakeoil/test -t /home/johndoe/snakeoil

来源:python -m unittest -h

  -s START, --start-directory START
                        Directory to start discovery ('.' default)
  -t TOP, --top-level-directory TOP
                        Top level directory of project (defaults to start
                        directory)

答案 18 :(得分:0)

如果您正在寻找仅命令行解决方案:

基于以下目录结构(一般带有专用的源目录):

new_project/
    src/
        antigravity.py
    test/
        test_antigravity.py

Windows :(在new_project中)

$ set PYTHONPATH=%PYTHONPATH%;%cd%\src
$ python -m unittest discover -s test

如果要在批处理for-loop中使用它,请参见this question

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添加更多目录。

答案 19 :(得分:0)

通过这种方式,您可以在任何地方运行测试脚本,而不必从命令行中弄乱系统变量。

这会将主项目文件夹添加到python路径,并找到相对于脚本本身而不是相对于当前工作目录的位置。

import sys, os

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

将其添加到所有测试脚本的顶部。这样会将主项目文件夹添加到系统路径,因此从那里开始工作的所有模块导入现在都可以工作。而且从哪里运行测试都没有关系。

您显然可以更改project_path_hack文件以匹配您的主项目文件夹位置。

答案 20 :(得分:0)

基于* nix的系统(macOS,Linux)的简单解决方案;以及Windows上的Git bash。

PYTHONPATH=$PWD python test/test_antigravity.py

print语句很容易工作,与pytest test/test_antigravity.py不同。一种“脚本”的完美方法,但并不是单元测试的真正方法。

当然,我想做一个适当的自动化测试,我会考虑使用适当的设置来pytest

答案 21 :(得分:0)

您项目中的 unittest 有 setup.py 文件。试试:

python3 setup.py build

python3 setup.py develop --user

做配置路径的工作等等。试试吧!

答案 22 :(得分:-2)

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

使用pip install -e .在开发模式下安装软件包。这是一个非常好的做法,由pytest推荐(参见他们的good practices documentation,你可以在其中找到两个项目布局)。

相关问题