标签归档:unit-testing

用Python编写单元测试:如何开始?[关闭]

问题:用Python编写单元测试:如何开始?[关闭]

我用Python完成了第一个适当的项目,现在的任务是为它编写测试。

由于这是我第一次做项目,所以这是我第一次为此编写测试。

问题是,我该如何开始?我真的一点儿都不知道。谁能指出我一些可以用来开始编写测试的文档/教程/链接/书(尤其是单元测试)

关于该主题的任何建议都将受到欢迎。

I completed my first proper project in Python and now my task is to write tests for it.

Since this is the first time I did a project, this is the first time I would be writing tests for it.

The question is, how do I start? I have absolutely no idea. Can anyone point me to some documentation/ tutorial/ link/ book that I can use to start with writing tests (and I guess unit testing in particular)

Any advice will be welcomed on this topic.


回答 0

如果您是第一次使用单元测试,那么最简单的学习方法通​​常是最好的。在此基础上,我建议使用py.test而不是默认unittest模块

考虑以下两个示例,它们具有相同的作用:

示例1(单元测试):

import unittest

class LearningCase(unittest.TestCase):
    def test_starting_out(self):
        self.assertEqual(1, 1)

def main():
    unittest.main()

if __name__ == "__main__":
    main()

示例2(pytest):

def test_starting_out():
    assert 1 == 1

假设两个文件都被命名test_unittesting.py,我们如何运行测试?

示例1(单元测试):

cd /path/to/dir/
python test_unittesting.py

示例2(pytest):

cd /path/to/dir/
py.test

If you’re brand new to using unittests, the simplest approach to learn is often the best. On that basis along I recommend using py.test rather than the default unittest module.

Consider these two examples, which do the same thing:

Example 1 (unittest):

import unittest

class LearningCase(unittest.TestCase):
    def test_starting_out(self):
        self.assertEqual(1, 1)

def main():
    unittest.main()

if __name__ == "__main__":
    main()

Example 2 (pytest):

def test_starting_out():
    assert 1 == 1

Assuming that both files are named test_unittesting.py, how do we run the tests?

Example 1 (unittest):

cd /path/to/dir/
python test_unittesting.py

Example 2 (pytest):

cd /path/to/dir/
py.test

回答 1

免费的Python书籍Dive Into Python中关于单元测试的,您可能会觉得很有用。

如果您遵循现代实践,则可能应该在编写项目时编写测试,而不要等到项目接近完成时再进行测试。

现在有点晚,但是现在您知道下一次了。:)

The free Python book Dive Into Python has a chapter on unit testing that you might find useful.

If you follow modern practices you should probably write the tests while you are writing your project, and not wait until your project is nearly finished.

Bit late now, but now you know for next time. :)


回答 2

在我看来,有三个很棒的python测试框架值得一试。
单元测试 -模块配备了所有的Python发行标准
鼻子 -可以运行单元测试的测试,而且具有更少的样板。
pytest-还运行单元测试,样板更少,报告更好,还有很多很棒的额外功能

为了对所有这些进行良好的比较,请阅读http://pythontesting.net/start-here的每个介绍。
也有关于灯具的扩展文章,还有更多。

There are, in my opinion, three great python testing frameworks that are good to check out.
unittest – module comes standard with all python distributions
nose – can run unittest tests, and has less boilerplate.
pytest – also runs unittest tests, has less boilerplate, better reporting, lots of cool extra features

To get a good comparison of all of these, read through the introductions to each at http://pythontesting.net/start-here.
There’s also extended articles on fixtures, and more there.


回答 3

的文档 单元测试将是一个良好的开端。

另外,现在有点晚了,但是将来请考虑在项目本身之前或期间编写单元测试。这样,您可以使用它们进行测试,并且(理论上)可以将它们用作回归测试,以验证您的代码更改没有破坏任何现有代码。这将为您带来编写测试用例的全部好处:)

The docs for unittest would be a good place to start.

Also, it is a bit late now, but in the future please consider writing unit tests before or during the project itself. That way you can use them to test as you go along, and (in theory) you can use them as regression tests, to verify that your code changes have not broken any existing code. This would give you the full benefit of writing test cases :)


回答 4

unittest随标准库一起提供,但是我建议您进行鼻子测试

鼻子扩展了单元测试,使测试更加容易。

我也建议你pylint

分析Python源代码以寻找错误和质量低劣的迹象。

unittest comes with the standard library, but I would recomend you nosetests.

nose extends unittest to make testing easier.

I would also recomend you pylint

analyzes Python source code looking for bugs and signs of poor quality.


回答 5

正如其他人已经回答的那样,编写单元测试已经晚了,但还不算太晚。问题是您的代码是否可测试。确实,要对现有代码进行测试并不容易,甚至有一本关于此的书:有效地使用遗留代码(请参见要点前期PDF)。

现在编写或不编写单元测试就是您的要求。您只需要意识到这可能是一项繁琐的任务。您可以解决此问题,以学习单元测试或考虑首先编写验收(端对端)测试,然后在更改代码或向项目添加新功能时开始编写单元测试。

As others already replied, it’s late to write unit tests, but not too late. The question is whether your code is testable or not. Indeed, it’s not easy to put existing code under test, there is even a book about this: Working Effectively with Legacy Code (see key points or precursor PDF).

Now writing the unit tests or not is your call. You just need to be aware that it could be a tedious task. You might tackle this to learn unit-testing or consider writing acceptance (end-to-end) tests first, and start writing unit tests when you’ll change the code or add new feature to the project.


回答 6

鼻子测试是在python中进行单元测试的出色解决方案。它同时支持基于单元测试的测试用例和doctest,并且只需简单的配置文件就可以开始使用它。

nosetests is brilliant solution for unit-testing in python. It supports both unittest based testcases and doctests, and gets you started with it with just simple config file.


Python单元测试去哪儿了?

问题: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.


您如何测试Python函数引发异常?

问题:您如何测试Python函数引发异常?

如何编写仅在函数未引发预期异常的情况下失败的单元测试?

How does one write a unittest that fails only if a function doesn’t throw an expected exception?


回答 0

使用unittest模块中的TestCase.assertRaises(或TestCase.failUnlessRaises),例如:

import mymod

class MyTestCase(unittest.TestCase):
    def test1(self):
        self.assertRaises(SomeCoolException, mymod.myfunc)

Use TestCase.assertRaises (or TestCase.failUnlessRaises) from the unittest module, for example:

import mymod

class MyTestCase(unittest.TestCase):
    def test1(self):
        self.assertRaises(SomeCoolException, mymod.myfunc)

回答 1

从Python 2.7开始,您可以使用上下文管理器来获取抛出的实际Exception对象:

import unittest

def broken_function():
    raise Exception('This is broken')

class MyTestCase(unittest.TestCase):
    def test(self):
        with self.assertRaises(Exception) as context:
            broken_function()

        self.assertTrue('This is broken' in context.exception)

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

http://docs.python.org/dev/library/unittest.html#unittest.TestCase.assertRaises


Python的3.5,你必须包装context.exceptionstr,否则,你会得到一个TypeError

self.assertTrue('This is broken' in str(context.exception))

Since Python 2.7 you can use context manager to get ahold of the actual Exception object thrown:

import unittest

def broken_function():
    raise Exception('This is broken')

class MyTestCase(unittest.TestCase):
    def test(self):
        with self.assertRaises(Exception) as context:
            broken_function()

        self.assertTrue('This is broken' in context.exception)

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

http://docs.python.org/dev/library/unittest.html#unittest.TestCase.assertRaises


In Python 3.5, you have to wrap context.exception in str, otherwise you’ll get a TypeError

self.assertTrue('This is broken' in str(context.exception))

回答 2

我上一个答案中的代码可以简化为:

def test_afunction_throws_exception(self):
    self.assertRaises(ExpectedException, afunction)

如果函数接受参数,则将它们传递给assertRaises,如下所示:

def test_afunction_throws_exception(self):
    self.assertRaises(ExpectedException, afunction, arg1, arg2)

The code in my previous answer can be simplified to:

def test_afunction_throws_exception(self):
    self.assertRaises(ExpectedException, afunction)

And if afunction takes arguments, just pass them into assertRaises like this:

def test_afunction_throws_exception(self):
    self.assertRaises(ExpectedException, afunction, arg1, arg2)

回答 3

您如何测试Python函数引发异常?

如何编写仅在函数未引发预期异常的情况下失败的测试?

简短答案:

将该self.assertRaises方法用作上下文管理器:

    def test_1_cannot_add_int_and_str(self):
        with self.assertRaises(TypeError):
            1 + '1'

示范

最佳实践方法相当容易在Python Shell中进行演示。

unittest

在Python 2.7或3中:

import unittest

在Python 2.6中,您可以安装2.7 unittest库的向后移植,称为unittest2,并将其别名为unittest

import unittest2 as unittest

测试示例

现在,将以下Python类型安全性测试粘贴到您的Python Shell中:

class MyTestCase(unittest.TestCase):
    def test_1_cannot_add_int_and_str(self):
        with self.assertRaises(TypeError):
            1 + '1'
    def test_2_cannot_add_int_and_str(self):
        import operator
        self.assertRaises(TypeError, operator.add, 1, '1')

测试人员assertRaises用作上下文管理器,以确保在记录错误的同时正确捕获并清除该错误。

我们也可以使用上下文管理器来编写它,请参阅测试二。第一个参数是您希望引发的错误类型,第二个参数是您要测试的函数,其余的args和关键字args将传递给该函数。

我认为仅使用上下文管理器就更加简单,可读性和可维护性。

运行测试

要运行测试:

unittest.main(exit=False)

在Python 2.6中,您可能需要以下内容

unittest.TextTestRunner().run(unittest.TestLoader().loadTestsFromTestCase(MyTestCase))

并且您的终端应输出以下内容:

..
----------------------------------------------------------------------
Ran 2 tests in 0.007s

OK
<unittest2.runner.TextTestResult run=2 errors=0 failures=0>

然后,正如我们期望的那样,尝试在中添加1'1'结果TypeError


有关更详细的输出,请尝试以下操作:

unittest.TextTestRunner(verbosity=2).run(unittest.TestLoader().loadTestsFromTestCase(MyTestCase))

How do you test that a Python function throws an exception?

How does one write a test that fails only if a function doesn’t throw an expected exception?

Short Answer:

Use the self.assertRaises method as a context manager:

    def test_1_cannot_add_int_and_str(self):
        with self.assertRaises(TypeError):
            1 + '1'

Demonstration

The best practice approach is fairly easy to demonstrate in a Python shell.

The unittest library

In Python 2.7 or 3:

import unittest

In Python 2.6, you can install a backport of 2.7’s unittest library, called unittest2, and just alias that as unittest:

import unittest2 as unittest

Example tests

Now, paste into your Python shell the following test of Python’s type-safety:

class MyTestCase(unittest.TestCase):
    def test_1_cannot_add_int_and_str(self):
        with self.assertRaises(TypeError):
            1 + '1'
    def test_2_cannot_add_int_and_str(self):
        import operator
        self.assertRaises(TypeError, operator.add, 1, '1')

Test one uses assertRaises as a context manager, which ensures that the error is properly caught and cleaned up, while recorded.

We could also write it without the context manager, see test two. The first argument would be the error type you expect to raise, the second argument, the function you are testing, and the remaining args and keyword args will be passed to that function.

I think it’s far more simple, readable, and maintainable to just to use the context manager.

Running the tests

To run the tests:

unittest.main(exit=False)

In Python 2.6, you’ll probably need the following:

unittest.TextTestRunner().run(unittest.TestLoader().loadTestsFromTestCase(MyTestCase))

And your terminal should output the following:

..
----------------------------------------------------------------------
Ran 2 tests in 0.007s

OK
<unittest2.runner.TextTestResult run=2 errors=0 failures=0>

And we see that as we expect, attempting to add a 1 and a '1' result in a TypeError.


For more verbose output, try this:

unittest.TextTestRunner(verbosity=2).run(unittest.TestLoader().loadTestsFromTestCase(MyTestCase))

回答 4

您的代码应遵循以下模式(这是一个unittest模块样式测试):

def test_afunction_throws_exception(self):
    try:
        afunction()
    except ExpectedException:
        pass
    except Exception:
       self.fail('unexpected exception raised')
    else:
       self.fail('ExpectedException not raised')

在Python <2.7上,此构造对于检查预期异常中的特定值很有用。unittest函数assertRaises仅检查是否引发了异常。

Your code should follow this pattern (this is a unittest module style test):

def test_afunction_throws_exception(self):
    try:
        afunction()
    except ExpectedException:
        pass
    except Exception:
       self.fail('unexpected exception raised')
    else:
       self.fail('ExpectedException not raised')

On Python < 2.7 this construct is useful for checking for specific values in the expected exception. The unittest function assertRaises only checks if an exception was raised.


回答 5

来自:http : //www.lengrand.fr/2011/12/pythonunittest-assertraises-raises-error/

首先,这是文件dum_function.py中相应的(still dum:p)函数:

def square_value(a):
   """
   Returns the square value of a.
   """
   try:
       out = a*a
   except TypeError:
       raise TypeError("Input should be a string:")

   return out

这是要执行的测试(仅插入此测试):

import dum_function as df # import function module
import unittest
class Test(unittest.TestCase):
   """
      The class inherits from unittest
      """
   def setUp(self):
       """
       This method is called before each test
       """
       self.false_int = "A"

   def tearDown(self):
       """
       This method is called after each test
       """
       pass
      #---
         ## TESTS
   def test_square_value(self):
       # assertRaises(excClass, callableObj) prototype
       self.assertRaises(TypeError, df.square_value(self.false_int))

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

现在我们准备测试我们的功能!这是尝试运行测试时发生的情况:

======================================================================
ERROR: test_square_value (__main__.Test)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test_dum_function.py", line 22, in test_square_value
    self.assertRaises(TypeError, df.square_value(self.false_int))
  File "/home/jlengrand/Desktop/function.py", line 8, in square_value
    raise TypeError("Input should be a string:")
TypeError: Input should be a string:

----------------------------------------------------------------------
Ran 1 test in 0.000s

FAILED (errors=1)

TypeError被引发actullay,并生成测试失败。问题在于,这正是我们想要的行为:s。

为避免此错误,只需在测试调用中使用lambda运行该函数:

self.assertRaises(TypeError, lambda: df.square_value(self.false_int))

最终输出:

----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

太好了!

…对我来说也是完美的!

非常感谢Julien Lengrand-Lambert先生


这个测试断言实际上返回一个假阳性。发生这种情况是因为’assertRaises’内部的lambda是引发类型错误而不是经过测试的函数的单位。

from: http://www.lengrand.fr/2011/12/pythonunittest-assertraises-raises-error/

First, here is the corresponding (still dum :p) function in file dum_function.py :

def square_value(a):
   """
   Returns the square value of a.
   """
   try:
       out = a*a
   except TypeError:
       raise TypeError("Input should be a string:")

   return out

Here is the test to be performed (only this test is inserted):

import dum_function as df # import function module
import unittest
class Test(unittest.TestCase):
   """
      The class inherits from unittest
      """
   def setUp(self):
       """
       This method is called before each test
       """
       self.false_int = "A"

   def tearDown(self):
       """
       This method is called after each test
       """
       pass
      #---
         ## TESTS
   def test_square_value(self):
       # assertRaises(excClass, callableObj) prototype
       self.assertRaises(TypeError, df.square_value(self.false_int))

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

We are now ready to test our function! Here is what happens when trying to run the test :

======================================================================
ERROR: test_square_value (__main__.Test)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test_dum_function.py", line 22, in test_square_value
    self.assertRaises(TypeError, df.square_value(self.false_int))
  File "/home/jlengrand/Desktop/function.py", line 8, in square_value
    raise TypeError("Input should be a string:")
TypeError: Input should be a string:

----------------------------------------------------------------------
Ran 1 test in 0.000s

FAILED (errors=1)

The TypeError is actullay raised, and generates a test failure. The problem is that this is exactly the behavior we wanted :s.

To avoid this error, simply run the function using lambda in the test call :

self.assertRaises(TypeError, lambda: df.square_value(self.false_int))

The final output :

----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

Perfect !

… and for me is perfect too!!

Thansk a lot Mr. Julien Lengrand-Lambert


This test assert actually returns a false positive. That happens because the lambda inside the ‘assertRaises’ is the unit that raises type error and not the tested function.


回答 6

您可以构建自己的程序contextmanager来检查是否引发了异常。

import contextlib

@contextlib.contextmanager
def raises(exception):
    try:
        yield 
    except exception as e:
        assert True
    else:
        assert False

然后您可以raises像这样使用:

with raises(Exception):
    print "Hola"  # Calls assert False

with raises(Exception):
    raise Exception  # Calls assert True

如果您使用pytest,则该东西已经实现。您可以pytest.raises(Exception)

例:

def test_div_zero():
    with pytest.raises(ZeroDivisionError):
        1/0

结果:

pigueiras@pigueiras$ py.test
================= test session starts =================
platform linux2 -- Python 2.6.6 -- py-1.4.20 -- pytest-2.5.2 -- /usr/bin/python
collected 1 items 

tests/test_div_zero.py:6: test_div_zero PASSED

You can build your own contextmanager to check if the exception was raised.

import contextlib

@contextlib.contextmanager
def raises(exception):
    try:
        yield 
    except exception as e:
        assert True
    else:
        assert False

And then you can use raises like this:

with raises(Exception):
    print "Hola"  # Calls assert False

with raises(Exception):
    raise Exception  # Calls assert True

If you are using pytest, this thing is implemented already. You can do pytest.raises(Exception):

Example:

def test_div_zero():
    with pytest.raises(ZeroDivisionError):
        1/0

And the result:

pigueiras@pigueiras$ py.test
================= test session starts =================
platform linux2 -- Python 2.6.6 -- py-1.4.20 -- pytest-2.5.2 -- /usr/bin/python
collected 1 items 

tests/test_div_zero.py:6: test_div_zero PASSED

回答 7

我几乎在所有地方都使用doctest [1],因为我喜欢同时记录和测试函数的事实。

看一下这段代码:

def throw_up(something, gowrong=False):
    """
    >>> throw_up('Fish n Chips')
    Traceback (most recent call last):
    ...
    Exception: Fish n Chips

    >>> throw_up('Fish n Chips', gowrong=True)
    'I feel fine!'
    """
    if gowrong:
        return "I feel fine!"
    raise Exception(something)

if __name__ == '__main__':
    import doctest
    doctest.testmod()

如果将此示例放在模块中并从命令行运行它,则将评估并检查两个测试用例。

[1] Python文档:23.2 doctest-测试交互式Python示例

I use doctest[1] almost everywhere because I like the fact that I document and test my functions at the same time.

Have a look at this code:

def throw_up(something, gowrong=False):
    """
    >>> throw_up('Fish n Chips')
    Traceback (most recent call last):
    ...
    Exception: Fish n Chips

    >>> throw_up('Fish n Chips', gowrong=True)
    'I feel fine!'
    """
    if gowrong:
        return "I feel fine!"
    raise Exception(something)

if __name__ == '__main__':
    import doctest
    doctest.testmod()

If you put this example in a module and run it from the command line both test cases are evaluated and checked.

[1] Python documentation: 23.2 doctest — Test interactive Python examples


回答 8

我刚刚发现,Mock库提供了assertRaisesWithMessage()方法(在其unittest.TestCase子类中),该方法不仅会检查是否引发了预期的异常,还检查了与预期消息一起引发的异常:

from testcase import TestCase

import mymod

class MyTestCase(TestCase):
    def test1(self):
        self.assertRaisesWithMessage(SomeCoolException,
                                     'expected message',
                                     mymod.myfunc)

I just discovered that the Mock library provides an assertRaisesWithMessage() method (in its unittest.TestCase subclass), which will check not only that the expected exception is raised, but also that it is raised with the expected message:

from testcase import TestCase

import mymod

class MyTestCase(TestCase):
    def test1(self):
        self.assertRaisesWithMessage(SomeCoolException,
                                     'expected message',
                                     mymod.myfunc)

回答 9

这里有很多答案。该代码显示了我们如何创建一个异常,如何在我们的方法中使用该异常,最后,您如何在单元测试中进行验证,并提出正确的异常。

import unittest

class DeviceException(Exception):
    def __init__(self, msg, code):
        self.msg = msg
        self.code = code
    def __str__(self):
        return repr("Error {}: {}".format(self.code, self.msg))

class MyDevice(object):
    def __init__(self):
        self.name = 'DefaultName'

    def setParameter(self, param, value):
        if isinstance(value, str):
            setattr(self, param , value)
        else:
            raise DeviceException('Incorrect type of argument passed. Name expects a string', 100001)

    def getParameter(self, param):
        return getattr(self, param)

class TestMyDevice(unittest.TestCase):

    def setUp(self):
        self.dev1 = MyDevice()

    def tearDown(self):
        del self.dev1

    def test_name(self):
        """ Test for valid input for name parameter """

        self.dev1.setParameter('name', 'MyDevice')
        name = self.dev1.getParameter('name')
        self.assertEqual(name, 'MyDevice')

    def test_invalid_name(self):
        """ Test to check if error is raised if invalid type of input is provided """

        self.assertRaises(DeviceException, self.dev1.setParameter, 'name', 1234)

    def test_exception_message(self):
        """ Test to check if correct exception message and code is raised when incorrect value is passed """

        with self.assertRaises(DeviceException) as cm:
            self.dev1.setParameter('name', 1234)
        self.assertEqual(cm.exception.msg, 'Incorrect type of argument passed. Name expects a string', 'mismatch in expected error message')
        self.assertEqual(cm.exception.code, 100001, 'mismatch in expected error code')


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

There are a lot of answers here. The code shows how we can create an Exception, how we can use that exception in our methods, and finally, how you can verify in a unit test, the correct exceptions being raised.

import unittest

class DeviceException(Exception):
    def __init__(self, msg, code):
        self.msg = msg
        self.code = code
    def __str__(self):
        return repr("Error {}: {}".format(self.code, self.msg))

class MyDevice(object):
    def __init__(self):
        self.name = 'DefaultName'

    def setParameter(self, param, value):
        if isinstance(value, str):
            setattr(self, param , value)
        else:
            raise DeviceException('Incorrect type of argument passed. Name expects a string', 100001)

    def getParameter(self, param):
        return getattr(self, param)

class TestMyDevice(unittest.TestCase):

    def setUp(self):
        self.dev1 = MyDevice()

    def tearDown(self):
        del self.dev1

    def test_name(self):
        """ Test for valid input for name parameter """

        self.dev1.setParameter('name', 'MyDevice')
        name = self.dev1.getParameter('name')
        self.assertEqual(name, 'MyDevice')

    def test_invalid_name(self):
        """ Test to check if error is raised if invalid type of input is provided """

        self.assertRaises(DeviceException, self.dev1.setParameter, 'name', 1234)

    def test_exception_message(self):
        """ Test to check if correct exception message and code is raised when incorrect value is passed """

        with self.assertRaises(DeviceException) as cm:
            self.dev1.setParameter('name', 1234)
        self.assertEqual(cm.exception.msg, 'Incorrect type of argument passed. Name expects a string', 'mismatch in expected error message')
        self.assertEqual(cm.exception.code, 100001, 'mismatch in expected error code')


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

回答 10

您可以使用unittest模块中的assertRaises

import unittest

class TestClass():
  def raises_exception(self):
    raise Exception("test")

class MyTestCase(unittest.TestCase):
  def test_if_method_raises_correct_exception(self):
    test_class = TestClass()
    # note that you dont use () when passing the method to assertRaises
    self.assertRaises(Exception, test_class.raises_exception)

You can use assertRaises from the unittest module

import unittest

class TestClass():
  def raises_exception(self):
    raise Exception("test")

class MyTestCase(unittest.TestCase):
  def test_if_method_raises_correct_exception(self):
    test_class = TestClass()
    # note that you dont use () when passing the method to assertRaises
    self.assertRaises(Exception, test_class.raises_exception)

回答 11

尽管所有答案都很好,但我仍在寻找一种方法来测试函数是否引发异常,而无需依赖于单元测试框架和编写测试类。

我最终写了以下内容:

def assert_error(e, x):
    try:
        e(x)
    except:
        return
    raise AssertionError()

def failing_function(x):
    raise ValueError()

def dummy_function(x):
    return x

if __name__=="__main__":
    assert_error(failing_function, 0)
    assert_error(dummy_function, 0)

它在正确的行失败:

Traceback (most recent call last):
  File "assert_error.py", line 16, in <module>
    assert_error(dummy_function, 0)
  File "assert_error.py", line 6, in assert_error
    raise AssertionError()
AssertionError

While all the answers are perfectly fine, I was looking for a way to test if a function raised an exception without relying on unit testing frameworks and having to write test classes.

I ended up writing the following:

def assert_error(e, x):
    try:
        e(x)
    except:
        return
    raise AssertionError()

def failing_function(x):
    raise ValueError()

def dummy_function(x):
    return x

if __name__=="__main__":
    assert_error(failing_function, 0)
    assert_error(dummy_function, 0)

And it fails on the right line :

Traceback (most recent call last):
  File "assert_error.py", line 16, in <module>
    assert_error(dummy_function, 0)
  File "assert_error.py", line 6, in assert_error
    raise AssertionError()
AssertionError

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

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

即使是一个简单的Python模块,最常见的目录结构似乎也是将单元测试分成各自的test目录:

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

例如,请参见此Python项目howto

我的问题是,实际上运行测试的通常方法什么?我怀疑这对除我以外的所有人来说都是显而易见的,但是您不能仅从python test_antigravity.pytest目录运行,import antigravity因为模块不在路径上,它将失败。

我知道我可以修改PYTHONPATH和其他与搜索路径有关的技巧,但我不敢相信这是最简单的方法-如果您是开发人员,这很好,但如果用户只是想检查测试结果,就不能期望用户使用通过。

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

那么,如果您刚刚将源代码下载到我的新项目中,将如何运行单元测试?我希望有一个答案让我对用户说:“要运行单元测试,请执行X。”

The very common directory structure for even a simple Python module seems to be to separate the unit tests into their own test directory:

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

for example see this Python project howto.

My question is simply What’s the usual way of actually running the tests? I suspect this is obvious to everyone except me, but you can’t just run python test_antigravity.py from the test directory as its import antigravity will fail as the module is not on the path.

I know I could modify PYTHONPATH and other search path related tricks, but I can’t believe that’s the simplest way – it’s fine if you’re the developer but not realistic to expect your users to use if they just want to check the tests are passing.

The other alternative is just to copy the test file into the other directory, but it seems a bit dumb and misses the point of having them in a separate directory to start with.

So, if you had just downloaded the source to my new project how would you run the unit tests? I’d prefer an answer that would let me say to my users: “To run the unit tests do X.”


回答 0

我认为最好的解决方案是使用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

The best solution in my opinion is to use the unittest command line interface which will add the directory to the sys.path so you don’t have to (done in the TestLoader class).

For example for a directory structure like this:

new_project
├── antigravity.py
└── test_antigravity.py

You can just run:

$ cd new_project
$ python -m unittest test_antigravity

For a directory structure like yours:

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

And in the test modules inside the test package, you can import the antigravity package and its modules as usual:

# 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

Running a single test module:

To run a single test module, in this case test_antigravity.py:

$ cd new_project
$ python -m unittest test.test_antigravity

Just reference the test module the same way you import it.

Running a single test case or test method:

Also you can run a single TestCase or a single test method:

$ python -m unittest test.test_antigravity.GravityTestCase
$ python -m unittest test.test_antigravity.GravityTestCase.test_method

Running all tests:

You can also use test discovery which will discover and run all the tests for you, they must be modules or packages named test*.py (can be changed with the -p, --pattern flag):

$ cd new_project
$ python -m unittest discover
$ # Also works without discover for Python 3
$ # as suggested by @Burrito in the comments
$ python -m unittest

This will run all the test*.py modules inside the test package.


回答 1

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

The simplest solution for your users is to provide an executable script (runtests.py or some such) which bootstraps the necessary test environment, including, if needed, adding your root project directory to sys.path temporarily. This doesn’t require users to set environment variables, something like this works fine in a bootstrap script:

import sys, os

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

Then your instructions to your users can be as simple as “python runtests.py“.

Of course, if the path you need really is os.path.dirname(__file__), then you don’t need to add it to sys.path at all; Python always puts the directory of the currently running script at the beginning of sys.path, so depending on your directory structure, just locating your runtests.py at the right place might be all that’s needed.

Also, the unittest module in Python 2.7+ (which is backported as unittest2 for Python 2.6 and earlier) now has test discovery built-in, so nose is no longer necessary if you want automated test discovery: your user instructions can be as simple as python -m unittest discover.


回答 2

我通常在项目目录(源目录和公用)下创建一个“运行测试”脚本,以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在测试模块中。缺点是您需要更多的支持代码来执行特定的测试……我每次都运行它们。

I generally create a “run tests” script in the project directory (the one that is common to both the source directory and test) that loads my “All Tests” suite. This is usually boilerplate code, so I can reuse it from project to project.

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 (from How do I run all Python unit tests in a directory?)

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

With this setup, you can indeed just include antigravity in your test modules. The downside is you would need more support code to execute a particular test… I just run them all every time.


回答 3

从您链接到的文章:

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

$ cd /path/to/googlemaps

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

$ python test/test_googlemaps.py

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

也许您应该按照提示看一下鼻子

From the article you linked to:

Create a test_modulename.py file and put your unittest tests in it. Since the test modules are in a separate directory from your code, you may need to add your module’s parent directory to your PYTHONPATH in order to run them:

$ cd /path/to/googlemaps

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

$ python test/test_googlemaps.py

Finally, there is one more popular unit testing framework for Python (it’s that important!), nose. nose helps simplify and extend the builtin unittest framework (it can, for example, automagically find your test code and setup your PYTHONPATH for you), but it is not included with the standard Python distribution.

Perhaps you should look at nose as it suggests?


回答 4

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

I had the same problem, with a separate unit tests folder. From the mentioned suggestions I add the absolute source path to sys.path.

The benefit of the following solution is, that one can run the file test/test_yourmodule.py without changing at first into the test-directory:

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

回答 5

如果您运行“ python setup.py development”,则该软件包将位于路径中。但是您可能不想这样做,因为您可能会感染系统python安装,这就是为什么存在virtualenvbuildout之类的工具的原因。

if you run “python setup.py develop” then the package will be in the path. But you may not want to do that because you could infect your system python installation, which is why tools like virtualenv and buildout exist.


回答 6

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)

Solution/Example for Python unittest module

Given the following project structure:

ProjectName
 ├── project_name
 |    ├── models
 |    |    └── thing_1.py
 |    └── __main__.py
 └── test
      ├── models
      |    └── test_thing_1.py
      └── __main__.py

You can run your project from the root directory with python project_name, which calls 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) Turn your test/models directory into a package by adding a __init__.py file. This makes the test cases within the sub directory accessible from the parent test directory.

# ProjectName/test/models/__init__.py

from .test_thing_1 import Thing1TestCase        

2) Modify your system path in test/__main__.py to include the project_name directory.

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

Now you can successfully import things from project_name in your tests.

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

回答 7

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

Use setup.py develop to make your working directory be part of the installed Python environment, then run the tests.


回答 8

如果您使用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:\您的\ PYTHON \ PROJECT \ ROOT_DIRECTORY

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

If you use VS Code and your tests are located on the same level as your project then running and debug your code doesn’t work out of the box. What you can do is change your launch.json file:

{
    "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",

In the root of your project add .env file

Inside of your .env file add path to the root of your project. This will temporarily add

PYTHONPATH=C:\YOUR\PYTHON\PROJECT\ROOT_DIRECTORY

path to your project and you will be able to use debug unit tests from VS Code


回答 9

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

python -m unittest discover -s ../test

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

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

I noticed that if you run the unittest command line interface from your “src” directory, then imports work correctly without modification.

python -m unittest discover -s ../test

If you want to put that in a batch file in your project directory, you can do this:

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

回答 10

我有相同的问题很长时间了。我最近选择的是以下目录结构:

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中,您将会丢失您的源文件夹)。自发现发现所有测试以来,脚本便会运行,但它们无法正常运行。因此,Makefile可以避免记住此问题。

我真的很喜欢这种方法,因为我不必触摸src文件夹,单元测试或环境变量,并且一切运行都非常顺利。

让我知道你们是否喜欢它。

希望能有所帮助,

I’ve had the same problem for a long time. What I recently chose is the following directory structure:

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

and in the __init__.py script of the test folder, I write the following:

import os
import sys
PROJECT_PATH = os.getcwd()
SOURCE_PATH = os.path.join(
    PROJECT_PATH,"src"
)
sys.path.append(SOURCE_PATH)

Super important for sharing the project is the Makefile, because it enforces running the scripts properly. Here is the command that I put in the Makefile:

run_tests:
    python -m unittest discover .

The Makefile is important not just because of the command it runs but also because of where it runs it from. If you would cd in tests and do python -m unittest discover ., it wouldn’t work because the init script in unit_tests calls os.getcwd(), which would then point to the incorrect absolute path (that would be appended to sys.path and you would be missing your source folder). The scripts would run since discover finds all the tests, but they wouldn’t run properly. So the Makefile is there to avoid having to remember this issue.

I really like this approach because I don’t have to touch my src folder, my unit tests or my environment variables and everything runs smoothly.

Let me know if you guys like it.

Hope that helps,


回答 11

以下是我的项目结构:

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

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

回答 12

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

我使用Python 3.6.2

cd new_project

pytest test/test_antigravity.py

要安装pytestsudo pip install pytest

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

我评论了这些东西if __name__ == '__main__'

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

What’s the usual way of actually running the tests

I use Python 3.6.2

cd new_project

pytest test/test_antigravity.py

To install pytest: sudo pip install pytest

I didn’t set any path variable and my imports are not failing with the same “test” project structure.

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

回答 13

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

例如:

./run_tests antigravity/*.py

或使用globlobtests/**/*.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单元测试?

It’s possible to use wrapper which runs selected or all tests.

For instance:

./run_tests antigravity/*.py

or to run all tests recursively use globbing (tests/**/*.py) (enable by shopt -s globstar).

The wrapper can basically use argparse to parse the arguments like:

parser = argparse.ArgumentParser()
parser.add_argument('files', nargs='*')

Then load all the tests:

for filename in args.files:
    exec(open(filename).read())

then add them into your test suite (using 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))

and run them:

result = unittest.TextTestRunner(verbosity=2).run(alltests)

Check this example for more details.

See also: How to run all Python unit tests in a directory?


回答 14

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即使为空也不会起作用。

Python 3+

Adding to @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

Mandatory don’t forget the __init__.py even if empty otherwise will not work.


回答 15

没有伏都教,您无法从父目录导入。这是至少与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”运行时,导入test.context失败,并且
  • 在new_project目录中使用“ python -m unittest”运行时,导入上下文失败。

有了这个技巧,他们俩都可以工作。

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

$ pwd
/projects/new_project
$ python -m unittest

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

$ cd test
$ python test_antigravity

好的,与其在test_antigravity.py中包含context.py的内容相比,没有什么漂亮,但也许有一点。欢迎提出建议。

You can’t import from the parent directory without some voodoo. Here’s yet another way that works with at least Python 3.6.

First, have a file test/context.py with the following content:

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

Then have the following import in the file test/test_antigravity.py:

import unittest
try:
    import context
except ModuleNotFoundError:
    import test.context    
import antigravity

Note that the reason for this try-except clause is that

  • 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.

Now you can run all the test files within test directory with:

$ pwd
/projects/new_project
$ python -m unittest

or run an individual test file with:

$ cd test
$ python test_antigravity

Ok, it’s not much prettier than having the content of context.py within test_antigravity.py, but maybe a little. Suggestions are welcome.


回答 16

如果测试目录中有多个目录,则必须在每个目录中添加一个__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)

If you have multiple directories in your test directory, then you have to add to each directory an __init__.py file.

/home/johndoe/snakeoil
└── test
    ├── __init__.py        
    └── frontend
        └── __init__.py
        └── test_foo.py
    └── backend
        └── __init__.py
        └── test_bar.py

Then to run every test at once, run:

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)

回答 17

无论您位于哪个工作目录中,此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在生产过程中,无需文件来负担您的包/内存开销。

This BASH script will execute the python unittest test directory from anywhere in the file system, no matter what working directory you are in.

This is useful when staying in the ./src or ./example working directory and you need a quick unit test:

#!/bin/bash

this_program="$0"
dirname="`dirname $this_program`"
readlink="`readlink -e $dirname`"

python -m unittest discover -s "$readlink"/test -v

No need for a test/__init__.py file to burden your package/memory-overhead during production.


回答 18

这样一来,您就可以从任何位置运行测试脚本,而不必从命令行中弄乱系统变量。

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

import sys, os

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

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

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

This way will let you run the test scripts from wherever you want without messing around with system variables from the command line.

This adds the main project folder to the python path, with the location found relative to the script itself, not relative to the current working directory.

import sys, os

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

Add that to the top of all your test scripts. That will add the main project folder to the system path, so any module imports that work from there will now work. And it doesn’t matter where you run the tests from.

You can obviously change the project_path_hack file to match your main project folder location.


回答 19

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

基于以下目录结构(一般带有专用的源目录):

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

Windows:(在中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添加更多目录。

If you are looking for a command line-only solution:

Based on the following directory structure (generalized with a dedicated source directory):

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

Windows: (in new_project)

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

See this question if you want to use this in a batch 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

With this approach, it is also possible to add more directories to the PYTHONPATH if necessary.


回答 20

您应该真正使用pip工具。

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

You should really use the pip tool.

Use pip install -e . to install your package in development mode. This is a very good practice, recommended by pytest (see their good practices documentation, where you can also find two project layouts to follow).


Pytest-pytest框架使编写小测试变得很容易,而且可以扩展以支持复杂的功能测试。

这个pytest框架使编写小型测试变得很容易,而且可以扩展以支持应用程序和库的复杂功能测试

以下是一个简单测试的示例:

# content of test_sample.py
def inc(x):
    return x + 1


def test_answer():
    assert inc(3) == 5

要执行它,请执行以下操作:

$ pytest
============================= test session starts =============================
collected 1 items

test_sample.py F

================================== FAILURES ===================================
_________________________________ test_answer _________________________________

    def test_answer():
>       assert inc(3) == 5
E       assert 4 == 5
E        +  where 4 = inc(3)

test_sample.py:5: AssertionError
========================== 1 failed in 0.04 seconds ===========================

由于pytest的详细断言反省,只有平淡assert使用语句。看见getting-started有关更多示例,请参阅

功能

文档

有关完整文档,包括安装、教程和PDF文档,请参阅https://docs.pytest.org/en/stable/

错误/请求

请使用GitHub issue tracker提交错误或请求功能

更改日志

请查阅Changelog有关每个版本的修复和增强功能的页面

支撑架最热

Open Collective是一个面向公开和透明社区的在线融资平台。它提供了在完全透明的情况下筹集资金和共享财务的工具

它是个人和公司的首选平台,他们想要直接向该项目进行一次性或每月捐款。

有关更多详细信息,请参阅pytest collective

对企业来说是最火爆的

作为Tidelift订阅的一部分提供

pytest和数千个其他包的维护者正在与Tidelift合作,为您用于构建应用程序的开源依赖项提供商业支持和维护。节省时间、降低风险并提高代码的健全性,同时付钱给您使用的确切依赖项的维护人员

Learn more.

安全性

pytest从未与安全漏洞相关联,但无论如何,若要报告安全漏洞,请使用Tidelift security contactTidelift将协调修复和披露

许可证

版权所有Holger Krekel和其他人,2004-2021

根据MIT许可,pytest是免费的开源软件