Python 单元测试在哪里? [关闭]

Posted

技术标签:

【中文标题】Python 单元测试在哪里? [关闭]【英文标题】:Where do the Python unit tests go? [closed] 【发布时间】:2010-09-08 20:04:38 【问题描述】:

如果您正在编写库或应用程序,那么单元测试文件在哪里?

将测试文件与主应用程序代码分开很好,但将它们放在应用程序根目录内的“测试”子目录中会很尴尬,因为它会使导入您将要测试的模块变得更加困难.

这里有最佳实践吗?

【问题讨论】:

【参考方案1】:

对于文件 module.py,单元测试通常应称为 test_module.py,遵循 Pythonic 命名约定。

有几个普遍接受的地方放test_module.py

    module.py在同一目录中。 在../tests/test_module.py(与代码目录同级)中。 在tests/test_module.py(代码目录下一层)。

我更喜欢#1,因为它可以简单地找到测试并导入它们。无论您使用什么构建系统,都可以轻松配置为运行以test_ 开头的文件。其实default unittest pattern used for test discovery is test*.py

【讨论】:

load_tests protocol 默认搜索名为 test*.py 的文件。另外,this top Google result 和 this unittest documentation 都使用 test_module.py 格式。 使用 foo_test.py 可以在使用制表符完成时节省一两次击键,因为您没有一堆以“test_”开头的文件。 @juniper,在您编写代码时,按照您的想法 module_test 会出现在自动完成中。这可能会令人困惑或烦人。 部署代码时,我们不想将测试部署到我们的生产位置。因此,在我们进行部署时,将它们放在一个目录 './tests/test_blah.py' 中很容易被拉出。此外,一些测试采用样本数据文件,将这些文件放在测试目录中至关重要,以免我们部署测试数据。 @KevinJ.Rice 您不应该测试代码在您的生产位置上是否有效吗?【参考方案2】:

只有 1 个测试文件

如果只有1个测试文件,建议放在***目录:

module/
    lib/
        __init__.py
        module.py
    test.py

在 CLI 中运行测试

python test.py

许多测试文件

如果有很多测试文件,把它放在tests文件夹中:

module/
    lib/
        __init__.py
        module.py
    tests/
        test_module.py
        test_module_function.py
# test_module.py

import unittest
from lib import module

class TestModule(unittest.TestCase):
    def test_module(self):
        pass

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

在 CLI 中运行测试

# In top-level /module/ folder
python -m tests.test_module
python -m tests.test_module_function

使用unittest discovery

unittest discovery 将在包文件夹中找到所有测试。

tests/ 文件夹中创建一个__init__.py

module/
    lib/
        __init__.py
        module.py
    tests/
        __init__.py
        test_module.py
        test_module_function.py

在 CLI 中运行测试

# In top-level /module/ folder

# -s, --start-directory (default current directory)
# -p, --pattern (default test*.py)

python -m unittest discover

参考

pytest Good Practices 用于测试布局 unittest

单元测试框架

nose nose2 pytest

【讨论】:

我的结构与您在“许多测试文件”下概述的结构相同,但是当我尝试 from lib import module 时出现错误:ImportError: No module named 'lib' @ShervinRad 你需要在module文件夹中运行,而不是libtests文件夹,python使用相对路径导入。【参考方案3】:

一种常见的做法是将测试目录与您的模块/包放在同一父目录中。因此,如果您的模块名为 foo.py,您的目录布局将如下所示:

parent_dir/
  foo.py
  tests/

当然,没有一种方法可以做到这一点。您还可以创建一个测试子目录并使用absolute import 导入模块。

无论您将测试放在哪里,我都建议您使用nose 来运行它们。 Nose 在您的目录中搜索测试。这样,您就可以将测试放在对组织最有意义的地方。

【讨论】:

我想这样做,但我做不到。要运行测试,我在 parent_dir 中,然后输入:“python tests\foo_test.py”,然后在 foo_test.py 中:“from ..foo import this, that, theother”失败并显示:“ValueError: Attempted非包中的相对导入” parent_dir 和测试都有一个 init.py,所以我不确定为什么它们不是包。我怀疑这是因为您从命令行运行的***脚本不能被视为(一部分)包,即使它位于带有 init.py 的目录中。那么如何运行测试呢?我今天在 Windows 上工作,祝福我的棉袜。 最好的方法——我发现——运行单元测试是安装你的库/程序,然后用鼻子运行单元测试。我会推荐 virtualenv 和 virtualenvwrapper 让这更容易。 @Tartley - 你需要一个 init.py 文件在你的 'tests' 目录中,才能让赦免导入工作。我有这种方法适用于鼻子,所以我不确定你为什么会遇到麻烦。 谢谢凯西 - 但我在所有相关目录中都有一个 init.py 文件。我不知道我做错了什么,但我所有的 Python 项目都有这个问题,我不明白为什么没有人这样做。天哪。 我的问题的一个解决方案,使用 Python2.6 或更高版本,我们使用以下命令从项目的根目录运行测试:python -m project.package.tests.module_tests(而不是 python project/包/测试/module_tests.py)。这会将测试模块的目录放在路径上,因此测试可以对其父目录进行相对导入以获取被测模块。【参考方案4】:

我们在编写 Pythoscope (https://pypi.org/project/pythoscope/) 时遇到了同样的问题,它为 Python 程序生成单元测试。在选择目录之前,我们在 python 列表中测试了人们,有很多不同的意见。最后我们选择在与源代码相同的目录下放置一个“tests”目录。在该目录中,我们为父目录中的每个模块生成一个测试文件。

【讨论】:

太棒了!刚刚在一些遗留代码上运行了 Pythoscope(很棒的标志),这太棒了。即时最佳实践,您可以让其他开发人员填写现在失败的测试存根。现在把它挂在竹子上? :)【参考方案5】:

我也倾向于将我的单元测试放在文件本身中,正如 Jeremy Cantrell 上面提到的那样,虽然我倾向于不将测试函数放在主体中,而是将所有内容放在一个

if __name__ == '__main__':
   do tests...

块。这最终将文档作为“示例代码”添加到文件中,以了解如何使用您正在测试的 python 文件。

我应该补充一点,我倾向于编写非常紧凑的模块/类。如果您的模块需要大量测试,您可以将它们放在另一个中,但即便如此,我仍然会添加:

if __name__ == '__main__':
   import tests.thisModule
   tests.thisModule.runtests

这让任何阅读您的源代码的人都知道在哪里寻找测试代码。

【讨论】:

“正如 Jeremy Cantrell 上面的注释”在哪里? 我喜欢这种工作方式。最简单的编辑器可以配置为使用热键运行您的文件,因此当您查看代码时,您可以立即运行测试。不幸的是,在 Python 以外的语言中,这看起来很糟糕,所以如果你在 C++ 或 Java 商店,你的同事可能会对此皱眉。它也不适用于代码覆盖工具。【参考方案6】:

每隔一段时间我就会发现自己检查了测试放置的主题,并且每次大多数人都建议在库代码旁边使用单独的文件夹结构,但我发现每次的论点都是相同的并且没有那么令人信服.我最终把我的测试模块放在核心模块旁边的某个地方。

这样做的主要原因是:重构

当我移动东西时,我确实希望测试模块与代码一起移动;如果它们在单独的树中,很容易丢失测试。老实说,迟早你会得到一个完全不同的文件夹结构,比如django、flask 等等。如果您不在乎,这很好。

你应该问自己的主要问题是:

我在写吗:

a) 可重用库或 b) 构建一个项目而不是将一些半分离的模块捆绑在一起?

如果是:

单独的文件夹和维护其结构的额外努力可能更适合。没有人会抱怨您的测试被部署到生产环境

但是,当测试与核心文件夹混合在一起时,也很容易将测试排除在分发之外; put this in the setup.py:

find_packages("src", exclude=["*.tests", "*.tests.*", "tests.*", "tests"]) 

如果 b:

您可能希望 — 就像我们每个人一样 — 您正在编写可重用的库,但大多数时候它们的生命与项目的生命息息相关。能够轻松维护您的项目应该是一个优先事项。

如果你做得很好并且你的模块非常适合另一个项目,它可能会被复制到这个新项目中——而不是分叉或制作成一个单独的库——并将位于它旁边的测试移动到与在单独的测试文件夹变得混乱的情况下进行测试相比,相同的文件夹结构很容易。 (您可能会争辩说它不应该是一团糟,但让我们现实一点)。

所以选择仍然是您的,但我认为通过混合测试,您可以实现与使用单独文件夹相同的所有内容,但在保持事物整洁方面花费更少的精力。

【讨论】:

实际上将测试部署到生产环境有什么问题?根据我的经验,它非常有用:允许用户在他们的环境中实际运行测试......当您收到错误报告时,您可以要求用户运行测试套件以确保一切正常并发送热补丁进行测试直接套件...此外,不是因为您将测试放在 module.tests 中,它最终投入生产,除非您在设置文件中做错了什么... 正如我在回答中所写的,我通常不会分开测试。但。将测试放入分发包可能会导致执行通常在生产环境中不需要的东西(例如,一些模块级代码)。这当然取决于测试是如何编写的,但为了安全起见,如果你不使用它们,没有人会错误地运行有害的东西。我不反对在发行版中包含测试,但我知道根据经验,它更安全。并且将它们放在单独的文件夹中可以非常容易地不将它们包含在 dist 中。【参考方案7】:

我使用tests/ 目录,然后使用相对导入来导入主要应用程序模块。所以在 MyApp/tests/foo.py 中,可能有:

from .. import foo

导入MyApp.foo 模块。

【讨论】:

"ValueError: Attempted relative import in non-package"【参考方案8】:

我不相信有既定的“最佳实践”。

我将测试放在应用程序代码之外的另一个目录中。然后,在运行所有测试之前,我将主应用程序目录添加到我的测试运行程序脚本(它也执行其他一些东西)中的 sys.path(允许您从任何地方导入模块)。这样一来,当我发布它时,我就不必从主代码中删除测试目录,如果数量很少的话,可以节省我的时间和精力。

【讨论】:

然后我将主应用目录添加到 sys.path从您的适当测试中。 这对我来说是这样的:os.sys.path.append(os.dirname('..'))【参考方案9】:

根据我在 Python 中开发测试框架的经验,我建议将 Python 单元测试放在单独的目录中。保持对称的目录结构。这将有助于仅打包核心库而不打包单元测试。下面通过示意图来实现。

                              <Main Package>
                               /          \
                              /            \
                            lib           tests
                            /                \
             [module1.py, module2.py,  [ut_module1.py, ut_module2.py,
              module3.py  module4.py,   ut_module3.py, ut_module.py]
              __init__.py]

这样,当您使用 rpm 打包这些库时,您可以只打包主库模块(仅)。这有助于可维护性,尤其是在敏捷环境中。

【讨论】:

我已经意识到这种方法的一个潜在优势是可以将测试开发(甚至可能是版本控制)作为它们自己的独立应用程序。当然,每一个优点都有一个缺点——保持对称性基本上是在做“复式记账”,并且使重构变得更加繁琐。 软后续问题:为什么敏捷环境特别适合这种方法? (即敏捷工作流如何使对称目录结构如此有益?)【参考方案10】:

我建议你在 GitHub 上查看一些主要的 Python 项目并获得一些想法。

当您的代码变得更大并且您添加更多库时,最好在您拥有 setup.py 的同一目录中创建一个测试文件夹,并为每种测试类型(单元测试、集成等)镜像您的项目目录结构

例如,如果您的目录结构如下:

myPackage/
    myapp/
       moduleA/
          __init__.py
          module_A.py
       moduleB/
          __init__.py
          module_B.py
setup.py

添加测试文件夹后,您将拥有如下目录结构:

myPackage/
    myapp/
       moduleA/
          __init__.py
          module_A.py
       moduleB/
          __init__.py
          module_B.py
test/
   unit/
      myapp/
         moduleA/
            module_A_test.py
         moduleB/
            module_B_test.py
   integration/
          myapp/
             moduleA/
                module_A_test.py
             moduleB/
                module_B_test.py
setup.py

许多正确编写的 Python 包使用相同的结构。一个很好的例子是 Boto 包。 检查https://github.com/boto/boto

【讨论】:

推荐使用“tests”而不是“test”,因为“test”是一个Python内置模块。 docs.python.org/2/library/test.html 并非总是如此。例如 matplotlib 位于 matplotlib/lib/matplotlib/tests (github.com/matplotlib/matplotlib/tree/…) 下,sklearn 位于 scikitelearn/sklearn/tests (github.com/scikit-learn/scikit-learn/tree/master/sklearn/tests) 下【参考方案11】:

我是怎么做到的……

文件夹结构:

project/
    src/
        code.py
    tests/
    setup.py

Setup.py 指向 src/ 作为包含我的项目模块的位置,然后我运行:

setup.py develop

它将我的项目添加到站点包中,指向我的工作副本。要运行我的测试,我使用:

setup.py tests

使用我配置的任何测试运行器。

【讨论】:

您似乎覆盖了“模块”一词。大多数 Python 程序员可能会认为该模块是您称为 code.py 的文件。将***目录称为“项目”会更有意义。【参考方案12】:

我更喜欢***测试目录。这确实意味着进口变得更加困难。为此,我有两个解决方案:

    使用设置工具。然后您可以将test_suite='tests.runalltests.suite' 传递给setup(),并且可以简单地运行测试:python setup.py test 在运行测试时设置 PYTHONPATH:PYTHONPATH=. python tests/runalltests.py

M2Crypto 中的代码如何支持这些东西:

http://svn.osafoundation.org/m2crypto/trunk/setup.py http://svn.osafoundation.org/m2crypto/trunk/tests/alltests.py

如果您更喜欢使用鼻子测试运行测试,您可能需要做一些不同的事情。

【讨论】:

【参考方案13】:

我将测试与被测代码 (CUT) 放在同一目录中。在我可以使用插件调整 pytest 的项目中,对于 foo.py,我使用 foo.pt 进行测试,这使得编辑特定模块及其测试变得非常容易:vi foo.*

如果我不能这样做,我会使用 foo_ut.py 或类似名称。你仍然可以使用vi foo*,不过如果存在foobar.pyfoobar_ut.py,它也会被捕获。

无论哪种情况,我都会调整测试发现过程以找到这些。

这将测试放在目录列表中的代码旁边,使测试在那里显而易见,并且使打开测试尽可能容易,因为它们位于单独的文件中。 (对于从命令行启动的编辑器,如上所述;对于 GUI 系统,单击代码文件和相邻(或非常接近)的测试文件。

作为others have pointed out,这也使重构和提取代码更容易,以便在需要时在其他地方使用。

我真的不喜欢将测试放在完全不同的目录树中的想法;为什么让开发人员在使用 CUT 打开文件时打开测试变得比必要的困难?并不是说绝大多数开发人员都热衷于编写或调整测试,以至于他们会忽略这样做的任何障碍,而不是以障碍为借口。 (根据我的经验,恰恰相反;即使你让它尽可能简单,我也知道许多开发人员懒得写测试。)

【讨论】:

【参考方案14】:

我们使用

app/src/code.py
app/testing/code_test.py 
app/docs/..

在每个测试文件中,我们将../src/ 插入sys.path。这不是最好的解决方案,但有效。我认为,如果有人想出类似 java 中的 maven 之类的东西,无论您从事什么项目,它都会为您提供可以正常工作的标准约定。

【讨论】:

【参考方案15】:

如果测试很简单,只需将它们放在文档字符串中——大多数 Python 测试框架都可以使用它:

>>> import module
>>> module.method('test')
'testresult'

对于其他更复杂的测试,我会将它们放在 ../tests/test_module.pytests/test_module.py 中。

【讨论】:

【参考方案16】:

在 C# 中,我通常将测试分成单独的程序集。

在 Python 中——到目前为止——我倾向于编写 doctest,其中测试位于函数的 docstring 中,或者将它们放在模块底部的 if __name__ == "__main__" 块中。

【讨论】:

【参考方案17】:

在编写名为“foo”的包时,我会将单元测试放入单独的包“foo_test”中。然后,模块和子包将与 SUT 包模块具有相同的名称。例如。在 foo_test.x.y 中可以找到模块 foo.x.y 的测试。然后每个测试包的 __init__.py 文件包含一个 AllTests 套件,其中包括该包的所有测试套件。 setuptools 提供了一种方便的方式来指定主要的测试包,这样在“python setup.py develop”之后就可以使用“python setup.py test”或者“python setup.py test -s foo_test.x.SomeTestSuite”只是一个特定的套件。

【讨论】:

【参考方案18】:

我最近开始使用 Python 进行编程,所以我还没有真正找到最佳实践的机会。 但是,我编写了一个模块,可以找到所有测试并运行它们。

所以,我有:

应用程序/ 应用文件.py 测试/ 应用文件测试.py

我必须看看随着我进行大型项目的进展情况如何。

【讨论】:

嗨,在 python 世界中,camelCase 有点奇怪。

以上是关于Python 单元测试在哪里? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

VS 2017 中的创建单元测试在哪里?

Maven 进行单元测试用例的使用,加载的资源是哪里的

Scala / Gradle 项目...在哪里存储单元测试所需的超大文件?

Meteor 中的单元测试应该放在哪里?

Python 单元测试 之setUP() 和 tearDown()

在编写单元测试时如何知道要测试啥? [关闭]