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

Maj*_*jor 641 python unit-testing

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

new_project/
    antigravity/
        antigravity.py
    test/
        test_antigravity.py
    setup.py
    etc.
Run Code Online (Sandbox Code Playgroud)

例如,看看这个Python项目如何.

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

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

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

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

Pie*_*rre 608

在我看来,最好的解决方案是使用unittest 命令行界面,它将目录添加到sys.path你不需要的(在TestLoader类中完成).

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

new_project
??? antigravity.py
??? test_antigravity.py
Run Code Online (Sandbox Code Playgroud)

你可以运行:

$ cd new_project
$ python -m unittest test_antigravity
Run Code Online (Sandbox Code Playgroud)

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

new_project
??? antigravity
?   ??? __init__.py         # make it a package
?   ??? antigravity.py
??? test
    ??? __init__.py         # also make test a package
    ??? test_antigravity.py
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

运行单个测试模块:

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

$ cd new_project
$ python -m unittest test.test_antigravity
Run Code Online (Sandbox Code Playgroud)

只需像导入它一样引用测试模块.

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

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

$ python -m unittest test.test_antigravity.GravityTestCase
$ python -m unittest test.test_antigravity.GravityTestCase.test_method
Run Code Online (Sandbox Code Playgroud)

运行所有测试:

您还可以使用将发现并运行所有测试的测试发现,它们必须是命名的模块或包test*.py(可以使用-p, --pattern标志更改):

$ cd new_project
$ python -m unittest discover
Run Code Online (Sandbox Code Playgroud)

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

  • `python -m unittest discover`将在`test`目录中找到并运行测试,如果它们被命名为`test*.py`.如果你命名子目录`tests`,使用`python -m unittest discover -s tests`,如果你命名测试文件`antigravity_test.py`,使用`python -m unittest discover -s tests -p'*test. py'`文件名可以使用下划线但不能使用短划线. (42认同)
  • 文件`test/__ init __.py`在这里至关重要,即使是空的 (13认同)
  • 由于与unittest库的测试子模块冲突,我在Python 3上失败了,错误是"ImportError:没有名为'test.test_antigravity'的模块".也许专家可以确认并将答案子目录名称更改为例如"测试"(复数). (7认同)
  • 我的```test_antigravity.py```仍然会导致```import antigravity```和```from antigravity import antigravity```的导入错误.我有```__init_.py```文件,我从```new project```目录调用```python3 -m unittest discover```.还有什么可能是错的? (7认同)
  • 如果 antigravity/antigravity.py 从同一 antigravity 目录下的模块导入,则这将不起作用。 (3认同)
  • 什么适用于python 3? (2认同)
  • @ Mike3d0g不确定你是否意味着暗示目录名称`test`是特殊的......但仅仅是为了记录,它不是.:P`python -m unittest discover`与`tests /`中的测试文件以及`test /`一起使用. (2认同)
  • @RCross 这是我遇到的问题。解决这个问题的办法是什么?antigravity/antigravity.py 如何在 antigravity 目录下导入某些内容并仍然与单独的 test/ 一起使用? (2认同)

Car*_*yer 47

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

import sys, os

sys.path.insert(0, os.path.dirname(__file__))
Run Code Online (Sandbox Code Playgroud)

然后,您对用户的指示可以像"python runtests.py"一样简单.

当然,如果您真正需要的路径是sys.path,那么您根本不需要添加它python runtests.py; Python始终将当前运行脚本的目录放在开头os.path.dirname(__file__),因此根据您的目录结构,只需找到您sys.path需要的所有内容即可.

此外,Python 2.7+中unittest模块(后向移植为Python 2.6及更早版本的unittest2)现在内置了测试发现,因此如果您想要自动化测试发现,则不再需要鼻子:您的用户指令可以像"python -m unittest discover".

  • @FredericBazin如果只需要单个测试或测试文件,请不要使用发现,只需命名要运行的模块即可。如果将其命名为模块点路径(而不是文件路径),则可以正确找出搜索路径。有关更多详细信息,请参见Peter的答案。 (2认同)

stw*_*dev 22

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

run_tests.py:

import unittest
import test.all_tests
testSuite = test.all_tests.create_test_suite()
text_runner = unittest.TextTestRunner().run(testSuite)
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

通过这种设置,您确实可以只include antigravity在您的测试模块中.缺点是你需要更多的支持代码来执行特定的测试...我只是每次都运行它们.

  • 我还想要*项目目录中的“运行测试”脚本*,并找到了[更简洁的方法](https://www.internalpointers.com/post/run-painless-test-suites-python-unittest) 来做它。强烈推荐。 (2认同)

Mar*_*ers 18

从您链接到的文章:

创建一个test_modulename.py文件并将您的unittest测试放入其中.由于测试模块与代码位于不同的目录中,因此您可能需要将模块的父目录添加到PYTHONPATH中以运行它们:

$ cd /path/to/googlemaps

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

$ python test/test_googlemaps.py
Run Code Online (Sandbox Code Playgroud)

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

也许你应该看看它暗示的鼻子

  • 那么在你完成了一百个项目后,你的python路径是什么样的?我应该手动进去清理我的路吗?如果是这样,这是一个可恶的设计! (4认同)
  • 是的,这适用于(对我来说),但我真的要求最简单的指令,我可以让用户使用我的模块来让他们运行测试.修改路径可能实际上是它,但我正在寻找更直接的东西. (3认同)

Pat*_*lva 15

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

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
Run Code Online (Sandbox Code Playgroud)

__init__.pytest 文件夹的脚本中,我写了以下内容:

import os
import sys
PROJECT_PATH = os.getcwd()
SOURCE_PATH = os.path.join(
    PROJECT_PATH,"src"
)
sys.path.append(SOURCE_PATH)
Run Code Online (Sandbox Code Playgroud)

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

run_tests:
    python -m unittest discover .
Run Code Online (Sandbox Code Playgroud)

Makefile 很重要,不仅因为它运行的命令,还因为它从哪里运行它。如果您将 cd in tests 和 do python -m unittest discover .,它将不起作用,因为unit_tests 中的init脚本调用 os.getcwd(),然后它将指向不正确的绝对路径(将附加到 sys.path 并且您将丢失您的源文件夹)。由于发现发现所有测试,脚本将运行,但它们无法正常运行。所以 Makefile 是为了避免记住这个问题。

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

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

希望有所帮助,

  • “如果我有时间我会更新我的答案” (30认同)
  • 今天怎么样?:) (7认同)
  • @PatrickDaSilva 似乎是一个复杂的解决方案。如果它不简单可能就不值得:-) (4认同)
  • 自从我写了这个答案以来,我找到了一种避免 sys.path.append 解决方法的方法。如果我有时间我会更新我的答案。 (2认同)

Tom*_*lis 9

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


and*_*pei 9

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

以下解决方案的好处是,可以在test/test_yourmodule.py不首先更改为test-directory的情况下运行该文件:

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
Run Code Online (Sandbox Code Playgroud)


eus*_*iro 7

Python 3+

\n\n

添加到@Pierre

\n\n

使用unittest这样的目录结构:

\n\n
new_project\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 antigravity\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 __init__.py         # make it a package\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 antigravity.py\n\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 test\n    \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 __init__.py         # also make test a package\n    \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 test_antigravity.py\n
Run Code Online (Sandbox Code Playgroud)\n\n

运行测试模块test_antigravity.py

\n\n
$ cd new_project\n$ python -m unittest test.test_antigravity\n
Run Code Online (Sandbox Code Playgroud)\n\n

或者单个TestCase

\n\n
$ python -m unittest test.test_antigravity.GravityTestCase\n
Run Code Online (Sandbox Code Playgroud)\n\n

强制不要忘记__init__.py即使为空,否则将不起作用。

\n


tjk*_*tjk 6

如果没有一些巫毒,你无法从父目录导入。这是至少适用于 Python 3.6 的另一种方法。

首先,有一个包含以下内容的文件 test/context.py:

import sys
import os
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
Run Code Online (Sandbox Code Playgroud)

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

import unittest
try:
    import context
except ModuleNotFoundError:
    import test.context    
import antigravity
Run Code Online (Sandbox Code Playgroud)

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

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

通过这种诡计,他们都起作用了。

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

$ pwd
/projects/new_project
$ python -m unittest
Run Code Online (Sandbox Code Playgroud)

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

$ cd test
$ python test_antigravity
Run Code Online (Sandbox Code Playgroud)

好吧,这并不比 test_antigravity.py 中包含 context.py 的内容漂亮多少,但也许有一点。欢迎提出建议。


Ned*_*der 5

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

  • 如果您有 [pip](https://python-packaging-user-guide.readthedocs.org/en/latest/quickstart.html#install-the-tools),您可以使用它在 ["editable " mode](http://www.pip-installer.org/en/latest/usage.html#pip-install): `pip install -e .` 这同样将包添加到 Python 环境中,而无需复制源代码,允许您继续编辑它所在的位置。 (2认同)

Vla*_*den 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"
            ]
        }    
    ]
}
Run Code Online (Sandbox Code Playgroud)

这里的关键是envFile

"envFile": "${workspaceRoot}/.env",
Run Code Online (Sandbox Code Playgroud)

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

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

PYTHONPATH = C:\您的\ PYTHON\PROJECT\ROOT_DIRECTORY

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


Der*_*ike 5

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

鉴于以下项目结构:

ProjectName
 ??? project_name
 |    ??? models
 |    |    ??? thing_1.py
 |    ??? __main__.py
 ??? test
      ??? models
      |    ??? test_thing_1.py
      ??? __main__.py
Run Code Online (Sandbox Code Playgroud)

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


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

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

# ProjectName/test/models/__init__.py

from .test_thing_1 import Thing1TestCase        
Run Code Online (Sandbox Code Playgroud)

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)
Run Code Online (Sandbox Code Playgroud)

现在,您可以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)
Run Code Online (Sandbox Code Playgroud)


Ala*_*n L 5

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

python -m unittest discover -s ../test
Run Code Online (Sandbox Code Playgroud)

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

setlocal & cd src & python -m unittest discover -s ../test
Run Code Online (Sandbox Code Playgroud)

  • 在本页的所有答案中,这是唯一对我有用的答案。我怀疑其他答案缺少一些重要信息。 (2认同)