使用典型的测试目录结构运行 unittest

2024-12-18 08:39:00
admin
原创
189
摘要:问题描述:即使是简单的 Python 模块,非常常见的目录结构似乎也是将单元测试分离到它们自己的test目录中:new_project/ antigravity/ antigravity.py test/ test_antigravity.py setu...

问题描述:

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

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

我的问题很简单:实际运行测试的常用方法是什么?我想除了我之外,这对每个人来说都是显而易见的,但您不能只python test_antigravity.py从测试目录运行,因为它import antigravity会失败,因为模块不在路径上。

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

另一种选择是将测试文件复制到其他目录中,但这似乎有点愚蠢,并且忽略了将它们放在单独目录中的意义。

那么,如果您刚刚将源代码下载到我的新项目中,您将如何运行单元测试?我更希望得到这样的答案:让我告诉我的用户:“要运行单元测试,请执行 X。”


解决方案 1:

我认为最好的解决方案是使用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
$ # Also works without discover for Python 3
$ # as suggested by @Burrito in the comments
$ python -m unittest

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

您可以在这里找到更新的官方文档discovery

解决方案 2:

我也遇到同样的问题很久了,最近我选择的目录结构如下:

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 并且您将丢失源文件夹)。脚本将运行,因为 discover 找到了所有测试,但它们无法正常运行。因此 Makefile 的存在是为了避免必须记住这个问题。

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

解决方案 3:

对于您的用户来说,最简单的解决方案是提供一个可执行脚本(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 )现在具有内置的测试发现功能,因此如果您想要自动测试发现,nose 不再是必要的:您的用户指令可以简单到python -m unittest discover

解决方案 4:

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

运行测试.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) \n              for name in module_strings]
    testSuite = unittest.TestSuite(suites)
    return testSuite

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

解决方案 5:

我遇到了同样的问题,有一个单独的单元测试文件夹。根据提到的建议,我将绝对源路径添加到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:

从您链接的文章中:

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

$ cd /path/to/googlemaps

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

$ python test/test_googlemaps.py

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

也许你应该按照它的建议看看鼻子?

解决方案 7:

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

python -m unittest discover -s ../test

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

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

解决方案 8:

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

鉴于以下项目结构:

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


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

1)通过添加文件将目录转换test/models为包__init__.py。这使得子目录中的测试用例可以从父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)

解决方案 9:

如果您运行“python setup.py evolve”,则包将位于路径中。但您可能不想这样做,因为这可能会感染系统 python 安装,这就是virtualenv和buildout等工具存在的原因。

解决方案 10:

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即使是空的否则将不起作用。

解决方案 11:

如果您使用 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 中的调试单元测试

解决方案 12:

如果没有一些巫术,您无法从父目录导入。这是另一种至少适用于 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”运行时,import test.context失败,并且

  • 从 new_project 目录使用“python -m unittest”运行时导入上下文失败。

通过这一诡计,他们都成功了。

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

$ pwd
/projects/new_project
$ python -m unittest

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

$ cd test
$ python test_antigravity

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

解决方案 13:

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

解决方案 14:

以下是我的项目结构:

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

解决方案 15:

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

例如:

./run_tests antigravity/*.py

或者使用globbing ( tests/**/*.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)

查看此示例以了解更多详细信息。

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

解决方案 16:

实际运行测试的通常方式是什么

我使用 Python 3.6.2

cd new_project

pytest test/test_antigravity.py

要安装pytestsudo pip install pytest

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

我注释掉了这些内容:if __name__ == '__main__'就像这样:

测试反重力.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()

解决方案 17:

如果您的测试目录中有多个目录,那么您必须向每个目录添加一个__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:

您确实应该使用 pip 工具。

用于pip install -e .在开发模式下安装包。这是 pytest 推荐的一种非常好的做法(请参阅他们的良好实践文档,您还可以在其中找到两种可遵循的项目布局)。

解决方案 19:

无论您位于什么工作目录,此 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文件来增加你的包装/内存开销。

解决方案 20:

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

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

import sys, os

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

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

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

解决方案 21:

针对基于 *nix 的系统(macOS、Linux)的简单解决方案;可能还包括 Windows 上的 Git bash。

PYTHONPATH=$PWD python test/test_antigravity.py

print语句很容易工作,不像pytest test/test_antigravity.py。对于“脚本”来说,这是一种完美的方式,但对于单元测试来说却不是。

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

解决方案 22:

作为cwd根项目目录(在您的情况下),您可以在不在任何目录中new_project运行以下命令:__init__.py

python -m unittest discover -s test

但你import需要test_antigravity.py

from antigravity import antigravity.your_object

而不是:

import antigravity.your_object

如果您不喜欢from antigravity该条款,您可能会喜欢Alan L 的回答。

解决方案 23:

在您的根目录或测试目录中创建一个conftest.py文件。

import sys
# Runs before all tests and imports when you run pytest
def pytest_configure() -> None:
    sys.path.append(__file__) # or __file__.parent for the above directory, or __file__ / 'src', etc.

如何运行测试:pytest从任何目录调用。


注意:我认为 99% 的情况下最好的方法就是告诉人们更新他们的 PYTHONPATH(您已经让他们安装了 python+所有要求,只需告诉他们更新他们的.bashrc)。


注意2:pytest 可以运行 unittest。(我认为它通常更有用)。unittest 不支持conftest.py。您可以改用,__init__.py但根据我的经验,此文件并不总是先运行,有时在调用它之前会导入其他文件,具体取决于结构和调用命令的方式。

解决方案 24:

新手问题..

文件名问题

我也在谷歌上寻找此问题的答案,但对我的情况没有任何帮助。

我使用的是 Windows,unittest 无法发现模块的原因是文件名中使用了连字符。将文件名重命名为下划线可以解决此问题

请参阅此 stackoverflow 问题

解决方案 25:

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

基于以下目录结构(以专用源目录进行概括):

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

窗户:(英寸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 添加更多目录。

解决方案 26:

你的项目中的 unittest 有setup.py一个文件。尝试:

python3 setup.py build

python3 setup.py develop --user

执行配置路径等的工作。试试看!

解决方案 27:

我认为https://docs.python-guide.org/writing/structure/中概述的方法非常简洁:

(此处逐字引用该篇文章)

为了给各个测试导入上下文,创建一个 tests/context.py 文件:

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

import sample

然后,在各个测试模块中,像这样导入模块:

from .context import sample

无论安装方法如何,它都会按预期工作。

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   2657  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1610  
  PLM(产品生命周期管理)流程管理在企业的产品研发与运营中扮演着至关重要的角色。随着市场环境的不断变化、技术的飞速发展以及客户需求的日益多样化,变更成为了企业在产品生命周期中不可避免的现象。有效的变更管理对于确保产品按时交付、控制成本、保证质量以及满足客户需求至关重要。本文将深入探讨PLM流程管理应对变更的四个关键机制...
plm系统简介   0  
  产品生命周期管理(PLM)和企业资源计划(ERP)是现代企业管理中至关重要的两个系统。它们在企业的运营和发展中发挥着不同但又相互关联的作用。理解这两个系统的区别,对于企业准确选择和应用适合自身需求的管理工具,提升运营效率和竞争力具有重要意义。管理对象与范围PLM主要聚焦于产品的全生命周期管理。从产品的概念设计阶段开始,...
plm流程是什么   0  
  产品配置管理在企业产品研发与生产过程中扮演着至关重要的角色,它确保产品能够满足不同客户的多样化需求,同时有效控制成本和提高生产效率。PLM(产品生命周期管理)系统作为整合产品全生命周期信息的关键平台,对产品配置管理提供了强大的支持。随着技术的不断发展,到 2025 年,有四大关键技术将进一步提升 PLM 系统对产品配置...
plm系统   0  
热门文章
项目管理软件有哪些?
曾咪二维码

扫码咨询,免费领取项目管理大礼包!

云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用