相关文章推荐
不拘小节的米饭  ·  Tutorial: ML ...·  5 天前    · 
老实的小摩托  ·  Task.Delay 方法 ...·  2 月前    · 
冷冷的牛排  ·  Postgresql + ...·  1 年前    · 
儒雅的椅子  ·  ControlNet ...·  1 年前    · 
高兴的烈酒  ·  使用js ...·  1 年前    · 

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

922 人关注

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

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

My question is simply 实际运行测试的通常方式是什么?我怀疑这对除了我之外的所有人来说都是显而易见的,但你不能只是从测试目录中运行python test_antigravity.py,因为它的import antigravity会失败,因为模块不在路径上。

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

另一个办法是直接把测试文件复制到另一个目录中,但这似乎有点愚蠢,而且错过了一开始就把它们放在一个单独目录中的意义。

那么,如果你刚刚下载了我的新项目的源代码,你将如何运行单元测试?我更喜欢一个能让我对我的用户说的答案。"要运行单元测试,请执行X"。

3 个评论
@EMP 当你需要设置搜索路径时,正确的解决方案是......设置搜索路径。你期待的是什么样的解决方案?
@CarlMeyer 另一个更好的解决方案是使用 unittest 的命令行界面,如我的 回答如下 所以你不必在路径中添加目录。
我也一样。我刚刚开始为一个小小的Python项目编写我的第一个单元测试,并且花了几天时间试图推理出一个事实,即我不能轻易地运行一个测试,同时把我的源码放在src目录下,把测试放在测试目录下,似乎可以用任何现有的测试框架。我最终会接受事情,想出一个办法;但这是一个非常令人沮丧的介绍。(而且我是Python之外的单元测试老手。)
python
unit-testing
Major Major
Major Major
发布于 2009-12-14
23 个回答
Pierre
Pierre
发布于 2021-03-15
已采纳
0 人赞同

The best solution in my opinion is to use the 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 discovery它将为你发现并运行所有的测试,它们必须是名为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包内运行所有test*.py模块。

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' 。 文件名可以使用下划线,但不能使用破折号。
expz
我在Python 3上失败了,错误是 ImportError: No module named 'test.test_antigravity' ,因为与unittest库的test子模块有冲突。也许专家可以确认一下,把答案子目录的名字改为例如 "test"(复数)。
我的 test_antigravity.py 仍然对 import antigravity from antigravity import antigravity 抛出一个导入错误,也是如此。我有两个 __init_.py 文件,我从 new project 目录中调用 python3 -m unittest discover 。还有什么问题吗?
文件 test/__init__.py 在这里是至关重要的,即使是空的。
ryan
@Mike3d0g 不知道你是否想暗示目录名称 test 是特殊的......但只是为了记录,它不是。 :P python -m unittest discover tests/ 中的测试文件一起工作,就像 test/ 一样。
Carl Meyer
Carl Meyer
发布于 2021-03-15
0 人赞同

对你的用户来说,最简单的解决方案是提供一个可执行的脚本( 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+(它被回传为unittest2用于Python 2.6和更早版本)现在有test discovery内置的,所以如果你想要自动测试发现,鼻子就不再需要了:你的用户指令可以像python -m unittest discover一样简单。

我把一些测试放在一个子文件夹里,如 "Major Major"。 它们可以用python -m unittest discover运行,但我如何选择只运行其中一个。如果我运行python -m unittest tests/testxxxxx,则会因为路径问题而失败。既然dicovery模式可以解决所有问题,我希望有另一个技巧来解决路径问题,而不是你在第一点中建议的手工编码路径修复。
@FredericBazin 如果你只想要一个测试或测试文件,不要使用发现,只要命名你想运行的模块。如果你把它命名为模块的点状路径(而不是文件路径),它可以正确地计算出搜索路径。更多细节见彼得的回答。
在我不得不运行类似 python -m pdb tests\test_antigravity.py 的情况下,这个黑客是有用的。在pdb中,我执行了 sys.path.insert(0, "antigravity") ,它允许导入语句被解析,就像我在运行这个模块一样。
Patrick Da Silva
Patrick Da Silva
发布于 2021-03-15
0 人赞同

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

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的重要性不仅在于它所运行的命令,而且还在于where it runs it from.如果你在测试中使用cd并做python -m unittest discover .,它将不会工作,因为initunit_tests中的脚本调用os.getcwd(),这将指向不正确的绝对路径(这将被追加到sys.path,你将错过你的源文件夹)。脚本会运行,因为发现了所有的测试,但它们不会正常运行。所以Makefile的存在是为了避免记住这个问题。

我非常喜欢这种方法,因为我不必碰我的src文件夹、我的单元测试或我的环境变量,一切都能顺利地运行。

自从我写了这个答案后,我找到了一个避免sys.path.append工作法的方法。如果我有时间,我将更新我的答案。
"如果我找到时间,我将更新我的答案"
@JoaquínL.Robles :哈哈,是的,当我写下最后一条评论时,我刚刚开始我的CTO工作。没想到会有这么少的时间!
今天的情况如何?)
@PatrickDaSilva 看来你的解决方案很复杂。如果它不简单,可能就不值得了:-)
stw_dev
stw_dev
发布于 2021-03-15
0 人赞同

我一般会在项目目录下创建一个 "运行测试 "脚本(源代码目录和 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。缺点是你需要更多的支持代码来执行一个特定的测试...我只是每次都运行它们。

z33k
我还想 项目目录中的 run tests 脚本 并发现 更加清洁的方式 to do it. Highly recommended.
Mark Byers
Mark Byers
发布于 2021-03-15
0 人赞同

从你链接的文章来看。

创建一个 test_modulename.py 文件并 把你的 unittest 测试放在里面。由于 测试模块是在一个独立的 目录中,你可能需要 添加你的模块的父目录 到你的PYTHONPATH中,以便运行

$ cd /path/to/googlemaps
$ export PYTHONPATH=$PYTHONPATH:/path/to/googlemaps/googlemaps
$ python test/test_googlemaps.py
  

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

也许你应该看一下nose如其所言?

是的,这很有效(对我来说),但我真正要求的是最简单的指示,我可以给用户我的模块,让他们运行测试。修改路径可能就是这样,但我想寻找更直接的东西。
那么,在你做了上百个项目之后,你的Python路径是什么样子的?我应该手动进入并清理我的路径吗?如果是这样的话,这就是一个令人厌恶的设计!
admirableadmin
admirableadmin
发布于 2021-03-15
0 人赞同

我有同样的问题,有一个单独的单元测试文件夹。从提到的建议中,我添加了 绝对源路径 to 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
    
Derek Soike
Derek Soike
发布于 2021-03-15
0 人赞同

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

Given the following project structure:

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

To run your tests with python test, effectively running ProjectName/test/__main__.py, you need to do the following:

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)
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)
    
Tom Willis
Tom Willis
发布于 2021-03-15
0 人赞同

如果你运行 "python setup.py develop",那么该软件包就会出现在路径中。但你可能不想这样做,因为你可能会感染你的系统python安装,这就是为什么像 虚拟环境 buildout exist.

Alan L
Alan L
发布于 2021-03-15
0 人赞同

我注意到,如果你从 "src "目录下运行unittest命令行界面,那么导入工作就会正确进行,无需修改。

python -m unittest discover -s ../test

如果你想把它放在你的项目目录下的一个批处理文件中,你可以这样做。

setlocal & cd src & python -m unittest discover -s ../test
    
在这个页面上的所有答案中,这是唯一一个对我有用的答案。 我怀疑其他答案缺少一些重要信息。
我们不得不这样做,真是愚蠢至极。但你能做什么呢。这是最简单和容易的解决方案
ta32
其他的答案都是依靠python的导入系统。在这个答案中,你为你的测试指定了路径,我认为测试运行器在运行测试之前会修改路径。
Vlad Bezden
Vlad Bezden
发布于 2021-03-15
0 人赞同

如果你使用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"

The key line here is envFile

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

在你项目的根部添加.env文件

在你的.env文件中添加到你的项目根部的路径。这将临时添加

pythonpath=c:your/python/project/root_directory

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

Ned Batchelder
Ned Batchelder
发布于 2021-03-15
0 人赞同

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

这让我得到了一个 invalid command 'develop' ,而如果我要求 setup.py --help-commands ,则没有提到这个选项。这是否需要在 setup.py 本身有一些东西才能发挥作用?
没关系--问题是我的 setup.py 文件中缺少一个 import setuptools 。但我想这确实表明,这对其他人的模块并不总是有效的。
如果你有 pip ,你可以用它来安装你的软件包在 "可编辑 "模式 :【替换代码0这同样也是将软件包添加到Python环境中,而不需要复制源码,允许你继续编辑它的位置。
替换代码0】和 python setup.py develop 是完全一样的,它只是将你的 setup.py 用猴子抓来使用setuptools,即使它实际上并不使用,所以无论怎样都可以使用。
kenorb
kenorb
发布于 2021-03-15
0 人赞同

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

./run_tests antigravity/*.py

或者用以下方法递归地运行所有测试globbing (tests/**/*.py) (enable by 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)

Check this更多的细节,请看例子。

另见。如何运行一个目录下的所有Python单元测试?

imbr
imbr
发布于 2021-03-15
0 人赞同

Python 3+

添加到@Pierre

Using unittest directory structure like this:

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

To run the test module test_antigravity.py:

$ cd new_project
$ python -m unittest test.test_antigravity

Or a single TestCase

$ python -m unittest test.test_antigravity.GravityTestCase

强制性 don't forget the __init__.py even if empty otherwise will not work.

tjk
tjk
发布于 2021-03-15
0 人赞同

如果没有一些巫术,你就不能从父目录导入。这里有另一种方法,至少可以在 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
    import context
except ModuleNotFoundError:
    import test.context    
import antigravity

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

  • import test.context fails when run with "python test_antigravity.py" and
  • import context fails when run with "python -m unittest" from the new_project directory.
  • With this trickery they both work.

    现在你可以运行所有 the test files within test directory with:

    $ pwd
    /projects/new_project
    $ python -m unittest
    

    或运行一个单独的测试文件,用。

    $ cd test
    $ python test_antigravity
    

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

    rolika
    rolika
    发布于 2021-03-15
    0 人赞同

    Following is my project structure:

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

    I found it better to import in the setUp() method:

    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()
        
    0 人赞同

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

    I use Python 3.6.2

    cd new_project
    pytest test/test_antigravity.py
    

    要安装pytest: sudo pip install pytest

    我没有设置任何路径变量,我的导入在相同的 "测试 "项目结构下没有失败。

    I commented out this stuff: if __name__ == '__main__' like this:

    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()
        
    squid
    squid
    发布于 2021-03-15
    0 人赞同

    你真的应该使用管道工具。

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

    为什么要给这个答案降权?我看了已接受的答案,虽然它还不错,但 pytest 更适合运行测试,因为你得到的控制台输出是彩色的,有堆栈跟踪信息和详细的断言错误信息。
    Qlimax
    Qlimax
    发布于 2021-03-15
    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
    

    Source: 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)
        
    0 人赞同

    这个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文件来负担你的包装/纪念品-开销。

    chasmani
    chasmani
    发布于 2021-03-15
    0 人赞同

    这种方式可以让你从任何地方运行测试脚本,而不需要在命令行中乱用系统变量。

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

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

    把它添加到你所有测试脚本的顶部。这将把主项目文件夹添加到系统路径中,所以任何从那里导入的模块现在都能工作。而且你从哪里运行测试都无所谓。

    显然,你可以改变project_path_hack文件,以符合你的主项目文件夹的位置。

    Polv
    Polv
    发布于 2021-03-15
    0 人赞同

    基于*nix系统(macOS、Linux)的简单解决方案;也可能是Windows上的Git bash。

    PYTHONPATH=$PWD python test/test_antigravity.py
    

    print语句很容易工作,与pytest test/test_antigravity.py不同。对于 "脚本 "来说,这是一个完美的方法,但对于单元测试来说,并不是真正的。

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

    pj.dewitte
    pj.dewitte
    发布于 2021-03-15
    0 人赞同

    如果你正在寻找一个只用命令行的解决方案。

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

    new_project/
            antigravity.py
        test/
            test_antigravity.py
    

    Windows: (in new_project)

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

    See this question如果你想在一个批处理的for-loop中使用它。

    Linux: (in 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中增加更多的目录。

    david92
    david92
    发布于 2021-03-15
    0 人赞同

    unittest在你的项目中有 setup.py 文件。

    python3 setup.py build