Python单元测试在哪里?

Python单元测试在哪里?

Where do the Python unit tests go?

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

将测试文件与主应用程序代码分开是很好的,但是将它们放入应用程序根目录内的"test s"子目录是很难的,因为这样很难导入要测试的模块。

这里有最佳实践吗?


对于文件module.py,单元测试通常应按照pythonic命名约定称为test_module.py

有几个常见的地方可以放置test_module.py

  • module.py在同一目录中。
  • ../tests/test_module.py中(与代码目录处于同一级别)。
  • tests/test_module.py中(代码目录下的一级)。
  • 我更喜欢1,因为它简单地查找测试并导入它们。您使用的任何构建系统都可以轻松配置为运行从test_开始的文件。实际上,用于测试发现的默认unittest模式是test*.py


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

    1
    2
    3
    parent_dir/
      foo.py
      tests/

    当然,这是没有办法的。您还可以创建一个tests子目录并使用绝对导入导入模块。

    无论你把测试放在哪里,我都建议你用鼻子来运行它们。nose在目录中搜索测试。通过这种方式,您可以将测试组织到最有意义的地方。


    只有一个测试文件

    如果没有太多的测试文件,那么将其放在顶级目录中是很好的(我认为这是一种pythonic(推荐)方法):

    1
    2
    3
    4
    5
    module/
      lib/
        __init__.py
        module.py
      test.py

    许多测试文件

    如果有许多测试文件,请将其放在tests文件夹中:

    1
    2
    3
    4
    5
    6
    7
    module/
      lib/
        __init__.py
        module.py
      tests/
        test_module.py
        test_module2.py

    但是,如果您将测试放在tests文件夹中,则由于__main__无法导入相关模块,因此无法在cli中进行import ..lib测试,因此我们可以使用nose,或者将父目录添加到python导入路径中,为此,我将创建一个

    爱因斯坦

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    import sys
    import os

    # append module root directory to sys.path
    sys.path.append(
        os.path.dirname(
            os.path.dirname(
                os.path.abspath(__file__)
            )
        )
    )

    在里面

    1
    2
    3
    4
    module/
      tests/
        test_module.py
        env.py

    import env在测试导入模块之前

    测试模块

    1
    2
    3
    4
    5
    6
    7
    8
    import unittest
    # append parent directory to import path
    import env
    # now we can import the lib module
    from lib import module

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

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


    我也倾向于把我的单元测试放在文件中,正如Jeremy Cantrell上面提到的,虽然我倾向于不把测试功能放在主体中,而是把所有东西放在

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

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

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

    1
    2
    3
    if __name__ == '__main__':
       import tests.thisModule
       tests.thisModule.runtests

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


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

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

    当我四处移动东西时,我确实希望测试模块随代码一起移动;如果它们位于单独的树中,那么很容易丢失测试。老实说,你迟早会得到一个完全不同的文件夹结构,比如django、flask和其他许多文件夹。如果你不在乎,那就好了。

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

    我写的是:

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

    如果A:

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

    但是,当测试与核心文件夹混合时,也很容易将它们排除在分发之外;请将其放入setup.py:

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

    如果B:

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

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

    所以这个选择仍然是你的,但是我认为,通过混合测试,你可以实现与单独文件夹相同的所有功能,但是在保持事物整洁方面做得更少。


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

    1
    from .. import foo

    导入MyApp.foo模块。


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

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


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

    1
    2
    3
    4
    5
    6
    7
    8
                                  <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打包这些库时,您可以只打包主库模块(仅限)。这有助于维护性,特别是在敏捷环境中。


    我建议您检查一下Github上的一些主要的python项目并获得一些想法。

    当代码变大并且添加了更多的库时,最好在与setup.py相同的目录中创建一个测试文件夹,并为每个测试类型(unittest、integration,…)镜像项目目录结构。

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    myPackage/
        myapp/
           moduleA/
              __init__.py
              module_A.py
           moduleB/
              __init__.py
              module_B.py
    setup.py

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    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


    我是怎么做到的…

    文件夹结构:

    1
    2
    3
    4
    5
    project/
        src/
            code.py
        tests/
        setup.py

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

    1
    setup.py develop

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

    1
    setup.py tests

    使用我配置的测试运行程序。


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

  • 使用设置工具。然后您可以将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

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


    在C中,我通常将测试分成单独的组件。

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


    我们使用

    APP/SRC/CODE.PY

    应用程序/测试/代码 est.py

    APP/DOCS/…

    在每个测试文件中,我们在sys.path中插入".../src/"。这不是最好的解决方案,但有效。我认为如果有人出现在Wave/Maven之类的Java中,这将是非常棒的,无论你从事什么项目,都会给你标准的工作规范。


    如果测试很简单,只需将它们放在docstring中——大多数用于Python的测试框架都可以使用它:

    1
    2
    3
    >>> import module
    >>> module.method('test')
    'testresult'

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


    在编写一个名为"foo"的包时,我将把单元测试放入一个单独的包"foo-test"。然后,模块和子包将具有与SUT包模块相同的名称。例如,模块foo.x.y的测试可以在foo-test.x.y中找到。每个测试包的uu init_uuy文件包含一个包含包的所有测试套件的所有测试套件。安装工具提供了一种指定主测试包的方便方法,因此在"python setup.py development"之后,您只需将"python setup.py test"或"python setup.py test-s foo_test.x.sometestsuite"用于特定的套件。


    我将测试放在与被测代码(CUT)相同的目录中;对于foo.py,测试将放在foo_ut.py或类似目录中。(我调整测试发现过程以找到这些。)

    这将测试放在代码旁边的目录列表中,使测试明显存在,并使打开测试尽可能容易,因为它们可能位于单独的文件中。(对于命令行编辑器,vim foo*和使用图形文件系统浏览器时,只需单击剪切文件,然后单击紧邻的测试文件。)

    正如其他人所指出的,这也使得重构和提取代码变得更加容易,以便在必要时在其他地方使用。

    我真的不喜欢将测试放在完全不同的目录树中的想法;为什么让开发人员在打开带有切口的文件时更难打开测试?绝大多数开发人员不是如此热衷于编写或修改测试,以至于他们会忽略任何这样做的障碍,而不是将障碍作为借口。(恰恰相反,在我的经验中,即使你尽可能简单,我也知道许多开发人员不会费心编写测试。)


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

    所以,我有:

    1
    2
    3
    4
    app/
     appfile.py
    test/
     appfileTest.py

    我要看看随着我向更大的项目的进展情况。


    推荐阅读

      linux文件io命令?

      linux文件io命令?,系统,设备,时间,地址,平均,信息,服务,工具,报告,网络,嵌入

      改文件linux命令行?

      改文件linux命令行?,地址,系统,工作,信息,数字,文件,命令,设备,密码,权限,lin

      拷贝文件夹linux命令?

      拷贝文件夹linux命令?,系统,地址,服务,信息,密码,情况,单位,项目,命令,文件,l

      打包文件命令linux?

      打包文件命令linux?,系统,时间,工具,名称,命令,文件,目录,格式,表示,详细信

      linux删除命令文件夹?

      linux删除命令文件夹?,系统,数据,通用,文件夹,命令,文件,环境,百度,不了,名

      linux文件io命令?

      linux文件io命令?,系统,设备,时间,地址,平均,信息,服务,工具,报告,网络,嵌入

      linux中文件编辑命令?

      linux中文件编辑命令?,系统,工作,工具,信息,地址,发行,命令,第一,检测,基础,l

      linux文件格式的命令?

      linux文件格式的命令?,设备,命令,系统,文件,标准,代码,情况,电脑,平台,数据,l

      linux命令行编译文件?

      linux命令行编译文件?,代码,系统,项目,工具,网上,手机,电脑,地址,官网,文件,L

      linux下文件写入命令?

      linux下文件写入命令?,系统,信息,文件,名字,电脑,软件,目录,命令,内容,指令,l

      linux命令备份文件夹?

      linux命令备份文件夹?,设备,系统,文件,命令,备份,情况,做好,名字,灵活,数据,l

      linux命令deb文件?

      linux命令deb文件?,软件,位置,第一,系统,中心,工具,信息,发行,网站,管理系统

      linux文件夹删除命令?

      linux文件夹删除命令?,系统,命令,不了,档案,名称,通用,文件夹,文件,目录,指

      linux文件类基本命令?

      linux文件类基本命令?,系统,设备,工具,工作,基础,命令,文件,发行,管理,网络,L

      linux建个文件夹命令?

      linux建个文件夹命令?,系统,名字,命令,文件,文件夹,环境,不了,名称,数据,密

      linux命令添加文件?

      linux命令添加文件?,工作,简介,数据,系统,文件,命令,操作,文件名,内容,终端,l

      linux命令移除文件?

      linux命令移除文件?,系统,环境,不了,名称,命令,文件夹,数据,文件,终端,目录,

      linux文件输入命令?

      linux文件输入命令?,工作,系统,地址,信息,工具,位置,命令,设备,发行,首开,lin

      文件备份命令linux?

      文件备份命令linux?,网站,系统,设备,文件,软件,网络,工具,环境,数据,地址,lin

      linux遍历文件命令?

      linux遍历文件命令?,系统,数据,工具,文件,平台,信息,百度,位置,时间,适当,lin