问题:Python单元测试去哪儿了?

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

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

这里有最佳实践吗?

If you’re writing a library, or an app, where do the unit test files go?

It’s nice to separate the test files from the main app code, but it’s awkward to put them into a “tests” subdirectory inside of the app root directory, because it makes it harder to import the modules that you’ll be testing.

Is there a best practice here?


回答 0

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

有几个公认的地方test_module.py

  1. 与相同的目录中module.py
  2. 进入../tests/test_module.py(与代码目录处于同一级别)。
  3. tests/test_module.py(代码目录下的一级)。

我更喜欢#1,因为它可以轻松找到测试并将其导入。无论您使用哪种构建系统,都可以轻松地将其配置为运行以开头的文件test_。实际上,用于测试发现默认unittest模式是test*.py

For a file module.py, the unit test should normally be called test_module.py, following Pythonic naming conventions.

There are several commonly accepted places to put test_module.py:

  1. In the same directory as module.py.
  2. In ../tests/test_module.py (at the same level as the code directory).
  3. In tests/test_module.py (one level under the code directory).

I prefer #1 for its simplicity of finding the tests and importing them. Whatever build system you’re using can easily be configured to run files starting with test_. Actually, the default unittest pattern used for test discovery is test*.py.


回答 1

仅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 将在包文件夹中找到所有测试。

创建一个__init__.pyin tests/文件夹

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

参考

单元测试框架

Only 1 test file

If there has only 1 test files, putting it in a top-level directory is recommended:

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

Run the test in CLI

python test.py

Many test files

If has many test files, put it in a tests folder:

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

Run the test in CLI

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

Use unittest discovery

unittest discovery will find all test in package folder.

Create a __init__.py in tests/ folder

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

Run the test in CLI

# In top-level /module/ folder

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

python -m unittest discover

Reference

Unit test framework


回答 2

通常的做法是将tests目录放置在与模块/软件包相同的父目录中。因此,如果您的模块名为foo.py,则目录布局将如下所示:

parent_dir/
  foo.py
  tests/

当然,没有一种方法可以做到这一点。您也可以创建一个tests子目录,然后使用绝对导入导入模块。

无论您在哪里进行测试,我都建议您使用鼻子进行测试。鼻子会在您的目录中搜索测试。这样,您可以在组织上最有意义的地方进行测试。

A common practice is to put the tests directory in the same parent directory as your module/package. So if your module was called foo.py your directory layout would look like:

parent_dir/
  foo.py
  tests/

Of course there is no one way of doing it. You could also make a tests subdirectory and import the module using absolute import.

Wherever you put your tests, I would recommend you use nose to run them. Nose searches through your directories for tests. This way, you can put tests wherever they make the most sense organizationally.


回答 3

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

We had the very same question when writing Pythoscope (https://pypi.org/project/pythoscope/), which generates unit tests for Python programs. We polled people on the testing in python list before we chose a directory, there were many different opinions. In the end we chose to put a “tests” directory in the same directory as the source code. In that directory we generate a test file for each module in the parent directory.


回答 4

正如杰里米·坎特雷尔(Jeremy Cantrell)所述,我也倾向于将单元测试放在文件本身中,尽管我倾向于不将测试功能放在主体中,而是将所有内容放在一个文件中。

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

块。最后,将文档添加到文件中作为“示例代码”,以说明如何使用要测试的python文件。

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

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

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

I also tend to put my unit tests in the file itself, as Jeremy Cantrell above notes, although I tend to not put the test function in the main body, but rather put everything in an

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

block. This ends up adding documentation to the file as ‘example code’ for how to use the python file you are testing.

I should add, I tend to write very tight modules/classes. If your modules require very large numbers of tests, you can put them in another, but even then, I’d still add:

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

This lets anybody reading your source code know where to look for the test code.


回答 5

我偶尔会检查一下测试放置的主题,大多数人每次都在库代码旁边推荐一个单独的文件夹结构,但是我发现每次参数都相同且并不那么令人信服。我最终将测试模块放在核心模块旁边。

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

当我四处移动时,我确实希望测试模块随代码一起移动。如果测试位于单独的树中,则很容易丢失测试。老实说,迟早您会得到一个完全不同的文件夹结构,例如djangoflask和许多其他文件夹。如果您不在乎,那很好。

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

我在写:

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

如果一个:

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

但是,将测试与核心文件夹混合时,也可以将测试从分发中排除出去,这同样容易。把它放在setup.py中

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

如果b:

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

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

因此,选择仍然是您的选择,但我认为,通过混合测试,您可以实现与使用单独的文件夹相同的所有功能,但是可以使工作保持整洁。

Every once in a while I find myself checking out the topic of test placement, and every time the majority recommends a separate folder structure beside the library code, but I find that every time the arguments are the same and are not that convincing. I end up putting my test modules somewhere beside the core modules.

The main reason for doing this is: refactoring.

When I move things around I do want test modules to move with the code; it’s easy to lose tests if they are in a separate tree. Let’s be honest, sooner or later you end up with a totally different folder structure, like django, flask and many others. Which is fine if you don’t care.

The main question you should ask yourself is this:

Am I writing:

  • a) reusable library or
  • b) building a project than bundles together some semi-separated modules?

If a:

A separate folder and the extra effort to maintain its structure may be better suited. No one will complain about your tests getting deployed to production.

But it’s also just as easy to exclude tests from being distributed when they are mixed with the core folders; put this in the setup.py:

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

If b:

You may wish — as every one of us do — that you are writing reusable libraries, but most of the time their life is tied to the life of the project. Ability to easily maintain your project should be a priority.

Then if you did a good job and your module is a good fit for another project, it will probably get copied — not forked or made into a separate library — into this new project, and moving tests that lay beside it in the same folder structure is easy in comparison to fishing up tests in a mess that a separate test folder had become. (You may argue that it shouldn’t be a mess in the first place but let’s be realistic here).

So the choice is still yours, but I would argue that with mixed up tests you achieve all the same things as with a separate folder, but with less effort on keeping things tidy.


回答 6

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

from .. import foo

导入MyApp.foo模块。

I use a tests/ directory, and then import the main application modules using relative imports. So in MyApp/tests/foo.py, there might be:

from .. import foo

to import the MyApp.foo module.


回答 7

我认为没有公认的“最佳实践”。

我将测试放在应用程序代码之外的另一个目录中。然后,在运行所有测试之前,在测试运行器脚本(还执行其他一些操作)中,将主应用程序目录添加到sys.path中(允许您从任何位置导入模块)。这样,我发布时就不必从主代码中删除测试目录,从而节省了时间和精力。

I don’t believe there is an established “best practice”.

I put my tests in another directory outside of the app code. I then add the main app directory to sys.path (allowing you to import the modules from anywhere) in my test runner script (which does some other stuff as well) before running all the tests. This way I never have to remove the tests directory from the main code when I release it, saving me time and effort, if an ever so tiny amount.


回答 8

根据我在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打包这些库时,您可以仅打包主库模块(仅)。这有助于维护性,尤其是在敏捷环境中。

From my experience in developing Testing frameworks in Python, I would suggest to put python unit tests in a separate directory. Maintain a symmetric directory structure. This would be helpful in packaging just the core libraries and not package the unit tests. Below is implemented through a schematic diagram.

                              <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]

In this way when you package these libraries using an rpm, you can just package the main library modules (only). This helps maintainability particularly in agile environment.


回答 9

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

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

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

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

I recommend you check some main Python projects on GitHub and get some ideas.

When your code gets larger and you add more libraries it’s better to create a test folder in the same directory you have setup.py and mirror your project directory structure for each test type (unittest, integration, …)

For example if you have a directory structure like:

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

After adding test folder you will have a directory structure like:

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

Many properly written Python packages uses the same structure. A very good example is the Boto package. Check https://github.com/boto/boto


回答 10

我该怎么做…

资料夹结构:

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

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

setup.py develop

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

setup.py tests

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

How I do it…

Folder structure:

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

Setup.py points to src/ as the location containing my projects modules, then i run:

setup.py develop

Which adds my project into site-packages, pointing to my working copy. To run my tests i use:

setup.py tests

Using whichever test runner I’ve configured.


回答 11

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

  1. 使用setuptools。然后,您可以test_suite='tests.runalltests.suite'进入setup(),并可以简单地运行测试:python setup.py test
  2. 运行测试时设置PYTHONPATH: PYTHONPATH=. python tests/runalltests.py

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

如果您希望通过鼻子测试运行测试,则可能需要做一些不同的事情。

I prefer toplevel tests directory. This does mean imports become a little more difficult. For that I have two solutions:

  1. Use setuptools. Then you can pass test_suite='tests.runalltests.suite' into setup(), and can run the tests simply: python setup.py test
  2. Set PYTHONPATH when running the tests: PYTHONPATH=. python tests/runalltests.py

Here’s how that stuff is supported by code in M2Crypto:

If you prefer to run tests with nosetests you might need do something a little different.


回答 12

我们用

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

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

We use

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

In each test file we insert ../src/ in sys.path. It’s not the nicest solution but works. I think it would be great if someone came up w/ something like maven in java that gives you standard conventions that just work, no matter what project you work on.


回答 13

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

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

对于其他涉及更多的测试,我会将它们放在../tests/test_module.py或中tests/test_module.py

If the tests are simple, simply put them in the docstring — most of the test frameworks for Python will be able to use that:

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

For other more involved tests, I’d put them either in ../tests/test_module.py or in tests/test_module.py.


回答 14

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

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

In C#, I’ve generally separated the tests into a separate assembly.

In Python — so far — I’ve tended to either write doctests, where the test is in the docstring of a function, or put them in the if __name__ == "__main__" block at the bottom of the module.


回答 15

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

When writing a package called “foo”, I will put unit tests into a separate package “foo_test”. Modules and subpackages will then have the same name as the SUT package module. E.g. tests for a module foo.x.y are found in foo_test.x.y. The __init__.py files of each testing package then contain an AllTests suite that includes all test suites of the package. setuptools provides a convenient way to specify the main testing package, so that after “python setup.py develop” you can just use “python setup.py test” or “python setup.py test -s foo_test.x.SomeTestSuite” to the just a specific suite.


回答 16

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

这会将测试放在目录列表中的代码旁边,从而使测试很明显,并且使测试在单独文件中时的打开变得尽可能容易。(对于命令行编辑器,vim foo*以及在使用图形文件系统浏览器时,只需单击CUT文件,然后单击紧邻的测试文件。)

正如其他人指出的那样,如果需要的话,这也使得重构和提取代码以在其他地方使用变得更加容易。

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

I put my tests in the same directory as the code under test (CUT); for foo.py the tests will be in foo_ut.py or similar. (I tweak the test discovery process to find these.)

This puts the tests right beside the code in a directory listing, making it obvious that tests are there, and makes opening the tests as easy as it can possibly be when they’re in a separate file. (For command line editors, vim foo* and when using a graphical filesystem browser, just click on the CUT file and then the immediately adjacent test file.)

As others have pointed out, this also makes it easier to refactor and to extract the code for use elsewhere should that ever be necessary.

I really dislike the idea of putting tests in a completely different directory tree; why make it harder than necessary for developers to open up the tests when they’re opening the file with the CUT? It’s not like the vast majority of developers are so keen on writing or tweaking tests that they’ll ignore any barrier to doing that, instead of using the barrier as an excuse. (Quite the opposite, in my experience; even when you make it as easy as possible I know many developers who can’t be bothered to write tests.)


回答 17

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

所以我有:

应用/
 appfile.py
测试/
 appfileTest.py

我必须查看进展到更大项目时的情况。

I’ve recently started to program in Python, so I’ve not really had chance to find out best practice yet. But, I’ve written a module that goes and finds all the tests and runs them.

So, I have:

app/
 appfile.py
test/
 appfileTest.py

I’ll have to see how it goes as I progress to larger projects.


声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。