标签归档:pytest

如何在pytest中打印到控制台?

问题:如何在pytest中打印到控制台?

我正在尝试将TDD(测试驱动的开发)与 pytestpytest使用时不会print进入控制台print

我正在pytest my_tests.py运行它。

documentation似乎是说,它应该是默认的工作:http://pytest.org/latest/capture.html

但:

import myapplication as tum

class TestBlogger:

    @classmethod
    def setup_class(self):
        self.user = "alice"
        self.b = tum.Blogger(self.user)
        print "This should be printed, but it won't be!"

    def test_inherit(self):
        assert issubclass(tum.Blogger, tum.Site)
        links = self.b.get_links(posts)
        print len(links)   # This won't print either.

什么都没有打印到我的标准输出控制台上(只是正常的进度以及通过/失败的测试数量)。

我正在测试的脚本包含打印:

class Blogger(Site):
    get_links(self, posts):
        print len(posts)   # It won't get printed in the test.

unittest模块中,默认情况下会打印所有内容,这正是我所需要的。但是,我想用pytest出于其他原因。

有谁知道如何使打印报表显示出来?

I’m trying to use TDD (test-driven development) with pytest. pytest will not print to the console when I use print.

I am using pytest my_tests.py to run it.

The documentation seems to say that it should work by default: http://pytest.org/latest/capture.html

But:

import myapplication as tum

class TestBlogger:

    @classmethod
    def setup_class(self):
        self.user = "alice"
        self.b = tum.Blogger(self.user)
        print "This should be printed, but it won't be!"

    def test_inherit(self):
        assert issubclass(tum.Blogger, tum.Site)
        links = self.b.get_links(posts)
        print len(links)   # This won't print either.

Nothing gets printed to my standard output console (just the normal progress and how many many tests passed/failed).

And the script that I’m testing contains print:

class Blogger(Site):
    get_links(self, posts):
        print len(posts)   # It won't get printed in the test.

In unittest module, everything gets printed by default, which is exactly what I need. However, I wish to use pytest for other reasons.

Does anyone know how to make the print statements get shown?


回答 0

默认情况下,py.test捕获标准输出的结果,以便它可以控制其输出结果的方式。如果不这样做,它将喷出大量文本,而没有测试打印该文本的上下文。

但是,如果测试失败,它将在结果报告中包括一部分,以显示在该特定测试中打印出的标准内容。

例如,

def test_good():
    for i in range(1000):
        print(i)

def test_bad():
    print('this should fail!')
    assert False

结果如下:

>>> py.test tmp.py
============================= test session starts ==============================
platform darwin -- Python 2.7.6 -- py-1.4.20 -- pytest-2.5.2
plugins: cache, cov, pep8, xdist
collected 2 items

tmp.py .F

=================================== FAILURES ===================================
___________________________________ test_bad ___________________________________

    def test_bad():
        print('this should fail!')
>       assert False
E       assert False

tmp.py:7: AssertionError
------------------------------- Captured stdout --------------------------------
this should fail!
====================== 1 failed, 1 passed in 0.04 seconds ======================

注意该Captured stdout部分。

如果您希望print在执行语句时看到它们,可以将-s标志传递给py.test。但是,请注意,有时可能很难解析。

>>> py.test tmp.py -s
============================= test session starts ==============================
platform darwin -- Python 2.7.6 -- py-1.4.20 -- pytest-2.5.2
plugins: cache, cov, pep8, xdist
collected 2 items

tmp.py 0
1
2
3
... and so on ...
997
998
999
.this should fail!
F

=================================== FAILURES ===================================
___________________________________ test_bad ___________________________________

    def test_bad():
        print('this should fail!')
>       assert False
E       assert False

tmp.py:7: AssertionError
====================== 1 failed, 1 passed in 0.02 seconds ======================

By default, py.test captures the result of standard out so that it can control how it prints it out. If it didn’t do this, it would spew out a lot of text without the context of what test printed that text.

However, if a test fails, it will include a section in the resulting report that shows what was printed to standard out in that particular test.

For example,

def test_good():
    for i in range(1000):
        print(i)

def test_bad():
    print('this should fail!')
    assert False

Results in the following output:

>>> py.test tmp.py
============================= test session starts ==============================
platform darwin -- Python 2.7.6 -- py-1.4.20 -- pytest-2.5.2
plugins: cache, cov, pep8, xdist
collected 2 items

tmp.py .F

=================================== FAILURES ===================================
___________________________________ test_bad ___________________________________

    def test_bad():
        print('this should fail!')
>       assert False
E       assert False

tmp.py:7: AssertionError
------------------------------- Captured stdout --------------------------------
this should fail!
====================== 1 failed, 1 passed in 0.04 seconds ======================

Note the Captured stdout section.

If you would like to see print statements as they are executed, you can pass the -s flag to py.test. However, note that this can sometimes be difficult to parse.

>>> py.test tmp.py -s
============================= test session starts ==============================
platform darwin -- Python 2.7.6 -- py-1.4.20 -- pytest-2.5.2
plugins: cache, cov, pep8, xdist
collected 2 items

tmp.py 0
1
2
3
... and so on ...
997
998
999
.this should fail!
F

=================================== FAILURES ===================================
___________________________________ test_bad ___________________________________

    def test_bad():
        print('this should fail!')
>       assert False
E       assert False

tmp.py:7: AssertionError
====================== 1 failed, 1 passed in 0.02 seconds ======================

回答 1

using -s选项将打印所有功能的输出,可能太多了。

如果您需要特定的输出,则您提到的文档页面提供了一些建议:

  1. assert False, "dumb assert to make PyTest print my stuff"在函数的末尾插入,由于测试失败,您将看到输出。

  2. 您有PyTest传递给您的特殊对象,您可以将输出写入文件中以供日后检查,例如

    def test_good1(capsys):
        for i in range(5):
            print i
        out, err = capsys.readouterr()
        open("err.txt", "w").write(err)
        open("out.txt", "w").write(out)

    您可以在单独的标签中打开outerr文件,然后让编辑器为您自动刷新它,或者执行简单的py.test; cat out.txtshell命令来运行测试。

那是做事的一种骇人听闻的方式,但是可能正是您所需要的东西:毕竟,TDD意味着您会弄乱这些东西,并在准备就绪时保持干净整洁:-)。

Using -s option will print output of all functions, which may be too much.

If you need particular output, the doc page you mentioned offers few suggestions:

  1. Insert assert False, "dumb assert to make PyTest print my stuff" at the end of your function, and you will see your output due to failed test.

  2. You have special object passed to you by PyTest, and you can write the output into a file to inspect it later, like

    def test_good1(capsys):
        for i in range(5):
            print i
        out, err = capsys.readouterr()
        open("err.txt", "w").write(err)
        open("out.txt", "w").write(out)
    

    You can open the out and err files in a separate tab and let editor automatically refresh it for you, or do a simple py.test; cat out.txt shell command to run your test.

That is rather hackish way to do stuff, but may be it is the stuff you need: after all, TDD means you mess with stuff and leave it clean and silent when it’s ready :-).


回答 2

简短答案

使用-s选项:

pytest -s

详细答案

文档

在执行测试期间,将捕获发送到stdoutstderr的所有输出。如果测试或设置方法失败,则通常会显示其相应的捕获输出以及失败回溯。

pytest具有选项--capture=method,其中method是每个测试捕获方法,并且可以是下列之一:fdsysnopytest还具有-s是的快捷方式--capture=no的选项,该选项使您可以在控制台中查看打印语句。

pytest --capture=no     # show print statements in console
pytest -s               # equivalent to previous command

设置捕获方法或禁用捕获

有两种pytest执行捕获的方法:

  1. 文件描述符(FD)级别捕获(默认):将捕获所有对操作系统文件描述符1和2的写操作。

  2. sys级捕获:仅捕获对Python文件sys.stdout和sys.stderr的写入。不捕获对文件描述符的写入。

pytest -s            # disable all capturing
pytest --capture=sys # replace sys.stdout/stderr with in-mem files
pytest --capture=fd  # also point filedescriptors 1 and 2 to temp file

Short Answer

Use the -s option:

pytest -s

Detailed answer

From the docs:

During test execution any output sent to stdout and stderr is captured. If a test or a setup method fails its according captured output will usually be shown along with the failure traceback.

pytest has the option --capture=method in which method is per-test capturing method, and could be one of the following: fd, sys or no. pytest also has the option -s which is a shortcut for --capture=no, and this is the option that will allow you to see your print statements in the console.

pytest --capture=no     # show print statements in console
pytest -s               # equivalent to previous command

Setting capturing methods or disabling capturing

There are two ways in which pytest can perform capturing:

  1. file descriptor (FD) level capturing (default): All writes going to the operating system file descriptors 1 and 2 will be captured.

  2. sys level capturing: Only writes to Python files sys.stdout and sys.stderr will be captured. No capturing of writes to filedescriptors is performed.

pytest -s            # disable all capturing
pytest --capture=sys # replace sys.stdout/stderr with in-mem files
pytest --capture=fd  # also point filedescriptors 1 and 2 to temp file

回答 3

PyTest确实需要在忽略所有内容时打印有关跳过测试的重要警告。

我不想通过测试发送信号失败,所以我做了如下的修改:

def test_2_YellAboutBrokenAndMutedTests():
    import atexit
    def report():
        print C_patch.tidy_text("""
In silent mode PyTest breaks low level stream structure I work with, so
I cannot test if my functionality work fine. I skipped corresponding tests.
Run `py.test -s` to make sure everything is tested.""")
    if sys.stdout != sys.__stdout__:
        atexit.register(report)

atexit模块允许我 PyTest释放输出流打印内容。输出如下:

============================= test session starts ==============================
platform linux2 -- Python 2.7.3, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
rootdir: /media/Storage/henaro/smyth/Alchemist2-git/sources/C_patch, inifile: 
collected 15 items 

test_C_patch.py .....ssss....s.

===================== 10 passed, 5 skipped in 0.15 seconds =====================
In silent mode PyTest breaks low level stream structure I work with, so
I cannot test if my functionality work fine. I skipped corresponding tests.
Run `py.test -s` to make sure everything is tested.
~/.../sources/C_patch$

即使PyTest在静默模式下,消息也会被打印,如果您使用来运行东西,则消息不会被打印py.test -s,因此一切都已经过了很好的测试。

I needed to print important warning about skipped tests exactly when PyTest muted literally everything.

I didn’t want to fail a test to send a signal, so I did a hack as follow:

def test_2_YellAboutBrokenAndMutedTests():
    import atexit
    def report():
        print C_patch.tidy_text("""
In silent mode PyTest breaks low level stream structure I work with, so
I cannot test if my functionality work fine. I skipped corresponding tests.
Run `py.test -s` to make sure everything is tested.""")
    if sys.stdout != sys.__stdout__:
        atexit.register(report)

The atexit module allows me to print stuff after PyTest released the output streams. The output looks as follow:

============================= test session starts ==============================
platform linux2 -- Python 2.7.3, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
rootdir: /media/Storage/henaro/smyth/Alchemist2-git/sources/C_patch, inifile: 
collected 15 items 

test_C_patch.py .....ssss....s.

===================== 10 passed, 5 skipped in 0.15 seconds =====================
In silent mode PyTest breaks low level stream structure I work with, so
I cannot test if my functionality work fine. I skipped corresponding tests.
Run `py.test -s` to make sure everything is tested.
~/.../sources/C_patch$

Message is printed even when PyTest is in silent mode, and is not printed if you run stuff with py.test -s, so everything is tested nicely already.


回答 4

根据pytest docspytest --capture=sys应该可以工作。如果要在测试中捕获标准,请参考capsys装置。

According to the pytest docs, pytest --capture=sys should work. If you want to capture standard out inside a test, refer to the capsys fixture.


回答 5

我最初是来这里寻找如何PyTest在VSCode的控制台中运行/调试单元测试的同时进行打印的。这可以通过以下launch.json配置完成。给定.venv虚拟环境文件夹。

    "version": "0.2.0",
    "configurations": [
        {
            "name": "PyTest",
            "type": "python",
            "request": "launch",
            "stopOnEntry": false,
            "pythonPath": "${config:python.pythonPath}",
            "module": "pytest",
            "args": [
                "-sv"
            ],
            "cwd": "${workspaceRoot}",
            "env": {},
            "envFile": "${workspaceRoot}/.venv",
            "debugOptions": [
                "WaitOnAbnormalExit",
                "WaitOnNormalExit",
                "RedirectOutput"
            ]
        }
    ]
}

I originally came in here to find how to make PyTest print in VSCode’s console while running/debugging the unit test from there. This can be done with the following launch.json configuration. Given .venv the virtual environment folder.

    "version": "0.2.0",
    "configurations": [
        {
            "name": "PyTest",
            "type": "python",
            "request": "launch",
            "stopOnEntry": false,
            "pythonPath": "${config:python.pythonPath}",
            "module": "pytest",
            "args": [
                "-sv"
            ],
            "cwd": "${workspaceRoot}",
            "env": {},
            "envFile": "${workspaceRoot}/.venv",
            "debugOptions": [
                "WaitOnAbnormalExit",
                "WaitOnNormalExit",
                "RedirectOutput"
            ]
        }
    ]
}

打印测试执行时间并使用py.test固定慢速测试

问题:打印测试执行时间并使用py.test固定慢速测试

我正在使用py.test在CI服务器上运行单元测试。测试使用通过网络获取的外部资源。有时测试跑步者花费的时间太长,导致测试跑步者被中止。我不能在本地重复这些问题。

有没有一种方法可以使py.test打印出(缓慢)测试的执行时间,因此固定有问题的测试变得更加容易?

I am running unit tests on a CI server using py.test. Tests use external resources fetched over network. Sometimes test runner takes too long, causing test runner to be aborted. I cannot repeat the issues locally.

Is there a way to make py.test print out execution times of (slow) test, so pinning down problematic tests become easier?


回答 0

我不确定这是否可以解决您的问题,但是您可以在测试套件完成后通过--durations=N以打印最慢的N测试。

使用--durations=0打印所有。

I’m not sure this will solve your problem, but you can pass --durations=N to print the slowest N tests after the test suite finishes.

Use --durations=0 to print all.


回答 1

您可以使用 --durations

pytest --durations=0 — Show all times for tests and setup and teardown

pytest --durations=1 — Just show me the slowest

pytest --durations=50 — Slowest 50, with times,  etc

请参阅:https : //medium.com/@brianokken/pytest-durations-0-show-all-times-for-tests-and-setup-and-teardown-848dccac85db

或者:https : //docs.pytest.org/en/latest/usage.html#profiling-test-execution-duration

You can pass the number with --durations

pytest --durations=0 — Show all times for tests and setup and teardown

pytest --durations=1 — Just show me the slowest

pytest --durations=50 — Slowest 50, with times, … etc

Take refer in: https://medium.com/@brianokken/pytest-durations-0-show-all-times-for-tests-and-setup-and-teardown-848dccac85db

Or: https://docs.pytest.org/en/latest/usage.html#profiling-test-execution-duration


将参数传递给灯具功能

问题:将参数传递给灯具功能

我正在使用py.test来测试包装在python类MyTester中的某些DLL代码。为了进行验证,我需要在测试期间记录一些测试数据,然后再进行更多处理。由于我有许多test _…文件,因此我想对大多数测试重用测试器对象的创建(例如MyTester的实例)。

由于tester对象是获得DLL变量和函数的引用的对象,因此我需要将DLL变量列表传递给每个测试文件的tester对象(要记录的变量对于test_是相同的。 。文件)。列表的内容将用于记录指定的数据。

我的想法是像这样做:

import pytest

class MyTester():
    def __init__(self, arg = ["var0", "var1"]):
        self.arg = arg
        # self.use_arg_to_init_logging_part()

    def dothis(self):
        print "this"

    def dothat(self):
        print "that"

# located in conftest.py (because other test will reuse it)

@pytest.fixture()
def tester(request):
    """ create tester object """
    # how to use the list below for arg?
    _tester = MyTester()
    return _tester

# located in test_...py

# @pytest.mark.usefixtures("tester") 
class TestIt():

    # def __init__(self):
    #     self.args_for_tester = ["var1", "var2"]
    #     # how to pass this list to the tester fixture?

    def test_tc1(self, tester):
       tester.dothis()
       assert 0 # for demo purpose

    def test_tc2(self, tester):
       tester.dothat()
       assert 0 # for demo purpose

是否有可能实现这种目标,或者还有更优雅的方式?

通常,我可以使用某种设置功能(xUnit样式)针对每种测试方法执行此操作。但是我想获得某种重用。有谁知道灯具是否可以做到这一点?

我知道我可以做这样的事情:(来自文档)

@pytest.fixture(scope="module", params=["merlinux.eu", "mail.python.org"])

但是我需要直接在测试模块中进行参数化。 是否可以从测试模块访问灯具的params属性?

I am using py.test to test some DLL code wrapped in a python class MyTester. For validating purpose I need to log some test data during the tests and do more processing afterwards. As I have many test_… files I want to reuse the tester object creation (instance of MyTester) for most of my tests.

As the tester object is the one which got the references to the DLL’s variables and functions I need to pass a list of the DLL’s variables to the tester object for each of the test files (variables to be logged are the same for a test_… file). The content of the list is shall be used to log the specified data.

My idea is to do it somehow like this:

import pytest

class MyTester():
    def __init__(self, arg = ["var0", "var1"]):
        self.arg = arg
        # self.use_arg_to_init_logging_part()

    def dothis(self):
        print "this"

    def dothat(self):
        print "that"

# located in conftest.py (because other test will reuse it)

@pytest.fixture()
def tester(request):
    """ create tester object """
    # how to use the list below for arg?
    _tester = MyTester()
    return _tester

# located in test_...py

# @pytest.mark.usefixtures("tester") 
class TestIt():

    # def __init__(self):
    #     self.args_for_tester = ["var1", "var2"]
    #     # how to pass this list to the tester fixture?

    def test_tc1(self, tester):
       tester.dothis()
       assert 0 # for demo purpose

    def test_tc2(self, tester):
       tester.dothat()
       assert 0 # for demo purpose

Is it possible to achieve it like this or is there even a more elegant way?

Usually I could do it for each test method with some kind of setup function (xUnit-style). But I want to gain some kind of reuse. Does anyone know if this is possible with fixtures at all?

I know I can do something like this: (from the docs)

@pytest.fixture(scope="module", params=["merlinux.eu", "mail.python.org"])

But I need to the parametrization directly in the test module. Is it possible to access the params attribute of the fixture from the test module?


回答 0

更新:由于这是该问题的公认答案,并且有时仍然会被反对,因此我应该添加一个更新。尽管我的原始答案(如下)是在较旧版本的pytest中执行此操作的唯一方法,因为其他人已经指出 pytest现在支持对灯具进行间接参数化。例如,您可以执行以下操作(通过@imiric):

# test_parameterized_fixture.py
import pytest

class MyTester:
    def __init__(self, x):
        self.x = x

    def dothis(self):
        assert self.x

@pytest.fixture
def tester(request):
    """Create tester object"""
    return MyTester(request.param)


class TestIt:
    @pytest.mark.parametrize('tester', [True, False], indirect=['tester'])
    def test_tc1(self, tester):
       tester.dothis()
       assert 1
$ pytest -v test_parameterized_fixture.py
================================================================================= test session starts =================================================================================
platform cygwin -- Python 3.6.8, pytest-5.3.1, py-1.8.0, pluggy-0.13.1 -- /usr/bin/python3
cachedir: .pytest_cache
rootdir: .
collected 2 items

test_parameterized_fixture.py::TestIt::test_tc1[True] PASSED                                                                                                                    [ 50%]
test_parameterized_fixture.py::TestIt::test_tc1[False] FAILED

但是,尽管这种形式的间接参数化是明确的,但正如@Yukihiko Shinoda 指出的那样,它现在支持一种形式的隐式间接参数化(尽管我在官方文档中找不到对此的任何明显引用):

# test_parameterized_fixture2.py
import pytest

class MyTester:
    def __init__(self, x):
        self.x = x

    def dothis(self):
        assert self.x

@pytest.fixture
def tester(tester_arg):
    """Create tester object"""
    return MyTester(tester_arg)


class TestIt:
    @pytest.mark.parametrize('tester_arg', [True, False])
    def test_tc1(self, tester):
       tester.dothis()
       assert 1
$ pytest -v test_parameterized_fixture2.py
================================================================================= test session starts =================================================================================
platform cygwin -- Python 3.6.8, pytest-5.3.1, py-1.8.0, pluggy-0.13.1 -- /usr/bin/python3
cachedir: .pytest_cache
rootdir: .
collected 2 items

test_parameterized_fixture2.py::TestIt::test_tc1[True] PASSED                                                                                                                   [ 50%]
test_parameterized_fixture2.py::TestIt::test_tc1[False] FAILED

我不确切知道这种形式的语义是什么,但是似乎可以pytest.mark.parametrize识别出,尽管该test_tc1方法不接受名为的参数,但它使用tester_argtester夹具却可以,因此它通过tester夹具传递参数化的参数。


我有一个类似的问题-我有一个称为的夹具test_package,后来我希望能够在特定测试中运行该夹具时将可选参数传递给该夹具。例如:

@pytest.fixture()
def test_package(request, version='1.0'):
    ...
    request.addfinalizer(fin)
    ...
    return package

(对于这些目的,夹具是做什么的或返回的对象的类型无关紧要package)。

然后希望以某种方式在测试功能中使用此固定装置,这样我也可以指定该version固定装置的参数以用于该测试。尽管这可能是一个不错的功能,但目前尚不可能。

同时,很容易使我的夹具简单地返回一个函数,该函数完成夹具先前所做的所有工作,但允许我指定version参数:

@pytest.fixture()
def test_package(request):
    def make_test_package(version='1.0'):
        ...
        request.addfinalizer(fin)
        ...
        return test_package

    return make_test_package

现在,我可以在测试函数中使用它,例如:

def test_install_package(test_package):
    package = test_package(version='1.1')
    ...
    assert ...

等等。

OP的尝试解决方案朝着正确的方向发展,正如@ hpk42的答案所暗示的那样,MyTester.__init__可以仅存储对请求的引用,例如:

class MyTester(object):
    def __init__(self, request, arg=["var0", "var1"]):
        self.request = request
        self.arg = arg
        # self.use_arg_to_init_logging_part()

    def dothis(self):
        print "this"

    def dothat(self):
        print "that"

然后使用它来实现固定装置,例如:

@pytest.fixture()
def tester(request):
    """ create tester object """
    # how to use the list below for arg?
    _tester = MyTester(request)
    return _tester

如果需要,MyTester可以对类进行一些重构,以便.args在创建其属性后可以对其进行更新,以调整各个测试的行为。

Update: Since this the accepted answer to this question and still gets upvoted sometimes, I should add an update. Although my original answer (below) was the only way to do this in older versions of pytest as others have noted pytest now supports indirect parametrization of fixtures. For example you can do something like this (via @imiric):

# test_parameterized_fixture.py
import pytest

class MyTester:
    def __init__(self, x):
        self.x = x

    def dothis(self):
        assert self.x

@pytest.fixture
def tester(request):
    """Create tester object"""
    return MyTester(request.param)


class TestIt:
    @pytest.mark.parametrize('tester', [True, False], indirect=['tester'])
    def test_tc1(self, tester):
       tester.dothis()
       assert 1
$ pytest -v test_parameterized_fixture.py
================================================================================= test session starts =================================================================================
platform cygwin -- Python 3.6.8, pytest-5.3.1, py-1.8.0, pluggy-0.13.1 -- /usr/bin/python3
cachedir: .pytest_cache
rootdir: .
collected 2 items

test_parameterized_fixture.py::TestIt::test_tc1[True] PASSED                                                                                                                    [ 50%]
test_parameterized_fixture.py::TestIt::test_tc1[False] FAILED

However, although this form of indirect parametrization is explicit, as @Yukihiko Shinoda points out it now supports a form of implicit indirect parametrization (though I couldn’t find any obvious reference to this in the official docs):

# test_parameterized_fixture2.py
import pytest

class MyTester:
    def __init__(self, x):
        self.x = x

    def dothis(self):
        assert self.x

@pytest.fixture
def tester(tester_arg):
    """Create tester object"""
    return MyTester(tester_arg)


class TestIt:
    @pytest.mark.parametrize('tester_arg', [True, False])
    def test_tc1(self, tester):
       tester.dothis()
       assert 1
$ pytest -v test_parameterized_fixture2.py
================================================================================= test session starts =================================================================================
platform cygwin -- Python 3.6.8, pytest-5.3.1, py-1.8.0, pluggy-0.13.1 -- /usr/bin/python3
cachedir: .pytest_cache
rootdir: .
collected 2 items

test_parameterized_fixture2.py::TestIt::test_tc1[True] PASSED                                                                                                                   [ 50%]
test_parameterized_fixture2.py::TestIt::test_tc1[False] FAILED

I don’t know exactly what are the semantics of this form, but it seems that pytest.mark.parametrize recognizes that although the test_tc1 method does not take an argument named tester_arg, the tester fixture that it’s using does, so it passes the parametrized argument on through the tester fixture.


I had a similar problem–I have a fixture called test_package, and I later wanted to be able to pass an optional argument to that fixture when running it in specific tests. For example:

@pytest.fixture()
def test_package(request, version='1.0'):
    ...
    request.addfinalizer(fin)
    ...
    return package

(It doesn’t matter for these purposes what the fixture does or what type of object the returned package) is.

It would then be desirable to somehow use this fixture in a test function in such a way that I can also specify the version argument to that fixture to use with that test. This is currently not possible, though might make a nice feature.

In the meantime it was easy enough to make my fixture simply return a function that does all the work the fixture previously did, but allows me to specify the version argument:

@pytest.fixture()
def test_package(request):
    def make_test_package(version='1.0'):
        ...
        request.addfinalizer(fin)
        ...
        return test_package

    return make_test_package

Now I can use this in my test function like:

def test_install_package(test_package):
    package = test_package(version='1.1')
    ...
    assert ...

and so on.

The OP’s attempted solution was headed in the right direction, and as @hpk42’s answer suggests, the MyTester.__init__ could just store off a reference to the request like:

class MyTester(object):
    def __init__(self, request, arg=["var0", "var1"]):
        self.request = request
        self.arg = arg
        # self.use_arg_to_init_logging_part()

    def dothis(self):
        print "this"

    def dothat(self):
        print "that"

Then use this to implement the fixture like:

@pytest.fixture()
def tester(request):
    """ create tester object """
    # how to use the list below for arg?
    _tester = MyTester(request)
    return _tester

If desired the MyTester class could be restructured a bit so that its .args attribute can be updated after it has been created, to tweak the behavior for individual tests.


回答 1

实际上,py.test中通过间接参数化本身支持此功能。

就您而言,您将:

@pytest.fixture
def tester(request):
    """Create tester object"""
    return MyTester(request.param)


class TestIt:
    @pytest.mark.parametrize('tester', [['var1', 'var2']], indirect=True)
    def test_tc1(self, tester):
       tester.dothis()
       assert 1

This is actually supported natively in py.test via indirect parametrization.

In your case, you would have:

@pytest.fixture
def tester(request):
    """Create tester object"""
    return MyTester(request.param)


class TestIt:
    @pytest.mark.parametrize('tester', [['var1', 'var2']], indirect=True)
    def test_tc1(self, tester):
       tester.dothis()
       assert 1

回答 2

您可以从Fixture函数(从而从Tester类)访问请求的模块/类/函数,请参见与来自Fixture函数的请求测试上下文进行交互。因此,您可以在类或模块上声明一些参数,然后测试仪固定装置即可进行拾取。

You can access the requesting module/class/function from fixture functions (and thus from your Tester class), see interacting with requesting test context from a fixture function. So you could declare some parameters on a class or module and the tester fixture can pick it up.


回答 3

我找不到任何文档,但是,它似乎可以在最新版本的pytest中使用。

@pytest.fixture
def tester(tester_arg):
    """Create tester object"""
    return MyTester(tester_arg)


class TestIt:
    @pytest.mark.parametrize('tester_arg', [['var1', 'var2']])
    def test_tc1(self, tester):
       tester.dothis()
       assert 1

I couldn’t find any document, however, it seems to work in latest version of pytest.

@pytest.fixture
def tester(tester_arg):
    """Create tester object"""
    return MyTester(tester_arg)


class TestIt:
    @pytest.mark.parametrize('tester_arg', [['var1', 'var2']])
    def test_tc1(self, tester):
       tester.dothis()
       assert 1

回答 4

改善imiric的答案:解决此问题的另一种优雅方法是创建“参数夹具”。我个人更喜欢它的indirect功能pytest。此功能可从中获得pytest_cases,最初的想法由Sup3rGeo提出。

import pytest
from pytest_cases import param_fixture

# create a single parameter fixture
var = param_fixture("var", [['var1', 'var2']], ids=str)

@pytest.fixture
def tester(var):
    """Create tester object"""
    return MyTester(var)

class TestIt:
    def test_tc1(self, tester):
       tester.dothis()
       assert 1

请注意,这pytest-cases@pytest_fixture_plus允许您在灯具上使用参数化标记,并@cases_data允许您从单独模块中的函数中获取参数。有关详细信息,请参见doc。我是作者;)

To improve a little bit imiric’s answer: another elegant way to solve this problem is to create “parameter fixtures”. I personally prefer it over the indirect feature of pytest. This feature is available from pytest_cases, and the original idea was suggested by Sup3rGeo.

import pytest
from pytest_cases import param_fixture

# create a single parameter fixture
var = param_fixture("var", [['var1', 'var2']], ids=str)

@pytest.fixture
def tester(var):
    """Create tester object"""
    return MyTester(var)

class TestIt:
    def test_tc1(self, tester):
       tester.dothis()
       assert 1

Note that pytest-cases also provides @fixture that allow you to use parametrization marks directly on your fixtures instead of having to use @pytest.fixture(params=...)

from pytest_cases import fixture, parametrize

@fixture
@parametrize("var", [['var1', 'var2']], ids=str)
def tester(var):
    """Create tester object"""
    return MyTester(var)

and @parametrize_with_cases that allows you to source your parameters from “case functions” that may be grouped in a class or even a separate module. See doc for details. I’m the author by the way ;)


回答 5

我做了一个有趣的装饰器,可以编写如下的灯具:

@fixture_taking_arguments
def dog(request, /, name, age=69):
    return f"{name} the dog aged {age}"

在这里,您的左侧/还有其他固定装置,在右边,您可以使用以下参数提供:

@dog.arguments("Buddy", age=7)
def test_with_dog(dog):
    assert dog == "Buddy the dog aged 7"

这与函数参数的工作方式相同。如果不提供age参数,则使用默认值69。如果您不提供name或省略dog.arguments装饰器,则会得到常规的TypeError: dog() missing 1 required positional argument: 'name'。如果您有另一个接受参数的固定装置name,那么它与此不会冲突。

还支持异步装置。

此外,这为您提供了一个不错的设置计划:

$ pytest test_dogs_and_owners.py --setup-plan

SETUP    F dog['Buddy', age=7]
...
SETUP    F dog['Champion']
SETUP    F owner (fixtures used: dog)['John Travolta']

一个完整的例子:

from plugin import fixture_taking_arguments

@fixture_taking_arguments
def dog(request, /, name, age=69):
    return f"{name} the dog aged {age}"


@fixture_taking_arguments
def owner(request, dog, /, name="John Doe"):
    yield f"{name}, owner of {dog}"


@dog.arguments("Buddy", age=7)
def test_with_dog(dog):
    assert dog == "Buddy the dog aged 7"


@dog.arguments("Champion")
class TestChampion:
    def test_with_dog(self, dog):
        assert dog == "Champion the dog aged 69"

    def test_with_default_owner(self, owner, dog):
        assert owner == "John Doe, owner of Champion the dog aged 69"
        assert dog == "Champion the dog aged 69"

    @owner.arguments("John Travolta")
    def test_with_named_owner(self, owner):
        assert owner == "John Travolta, owner of Champion the dog aged 69"

装饰器的代码:

import pytest
from dataclasses import dataclass
from functools import wraps
from inspect import signature, Parameter, isgeneratorfunction, iscoroutinefunction, isasyncgenfunction
from itertools import zip_longest, chain


_NOTHING = object()


def _omittable_parentheses_decorator(decorator):
    @wraps(decorator)
    def wrapper(*args, **kwargs):
        if not kwargs and len(args) == 1 and callable(args[0]):
            return decorator()(args[0])
        else:
            return decorator(*args, **kwargs)
    return wrapper


@dataclass
class _ArgsKwargs:
    args: ...
    kwargs: ...

    def __repr__(self):
        return ", ".join(chain(
               (repr(v) for v in self.args), 
               (f"{k}={v!r}" for k, v in self.kwargs.items())))


def _flatten_arguments(sig, args, kwargs):
    assert len(sig.parameters) == len(args) + len(kwargs)
    for name, arg in zip_longest(sig.parameters, args, fillvalue=_NOTHING):
        yield arg if arg is not _NOTHING else kwargs[name]


def _get_actual_args_kwargs(sig, args, kwargs):
    request = kwargs["request"]
    try:
        request_args, request_kwargs = request.param.args, request.param.kwargs
    except AttributeError:
        request_args, request_kwargs = (), {}
    return tuple(_flatten_arguments(sig, args, kwargs)) + request_args, request_kwargs


@_omittable_parentheses_decorator
def fixture_taking_arguments(*pytest_fixture_args, **pytest_fixture_kwargs):
    def decorator(func):
        original_signature = signature(func)

        def new_parameters():
            for param in original_signature.parameters.values():
                if param.kind == Parameter.POSITIONAL_ONLY:
                    yield param.replace(kind=Parameter.POSITIONAL_OR_KEYWORD)

        new_signature = original_signature.replace(parameters=list(new_parameters()))

        if "request" not in new_signature.parameters:
            raise AttributeError("Target function must have positional-only argument `request`")

        is_async_generator = isasyncgenfunction(func)
        is_async = is_async_generator or iscoroutinefunction(func)
        is_generator = isgeneratorfunction(func)

        if is_async:
            @wraps(func)
            async def wrapper(*args, **kwargs):
                args, kwargs = _get_actual_args_kwargs(new_signature, args, kwargs)
                if is_async_generator:
                    async for result in func(*args, **kwargs):
                        yield result
                else:
                    yield await func(*args, **kwargs)
        else:
            @wraps(func)
            def wrapper(*args, **kwargs):
                args, kwargs = _get_actual_args_kwargs(new_signature, args, kwargs)
                if is_generator:
                    yield from func(*args, **kwargs)
                else:
                    yield func(*args, **kwargs)

        wrapper.__signature__ = new_signature
        fixture = pytest.fixture(*pytest_fixture_args, **pytest_fixture_kwargs)(wrapper)
        fixture_name = pytest_fixture_kwargs.get("name", fixture.__name__)

        def parametrizer(*args, **kwargs):
            return pytest.mark.parametrize(fixture_name, [_ArgsKwargs(args, kwargs)], indirect=True)

        fixture.arguments = parametrizer

        return fixture
    return decorator

I made a funny decorator that allows writing fixtures like this:

@fixture_taking_arguments
def dog(request, /, name, age=69):
    return f"{name} the dog aged {age}"

Here, to the left of / you have other fixtures, and to the right you have parameters that are supplied using:

@dog.arguments("Buddy", age=7)
def test_with_dog(dog):
    assert dog == "Buddy the dog aged 7"

This works the same way function arguments work. If you don’t supply the age argument, the default one, 69, is used instead. if you don’t supply name, or omit the dog.arguments decorator, you get the regular TypeError: dog() missing 1 required positional argument: 'name'. If you have another fixture that takes argument name, it doesn’t conflict with this one.

Async fixtures are also supported.

Additionally, this gives you a nice setup plan:

$ pytest test_dogs_and_owners.py --setup-plan

SETUP    F dog['Buddy', age=7]
...
SETUP    F dog['Champion']
SETUP    F owner (fixtures used: dog)['John Travolta']

A full example:

from plugin import fixture_taking_arguments

@fixture_taking_arguments
def dog(request, /, name, age=69):
    return f"{name} the dog aged {age}"


@fixture_taking_arguments
def owner(request, dog, /, name="John Doe"):
    yield f"{name}, owner of {dog}"


@dog.arguments("Buddy", age=7)
def test_with_dog(dog):
    assert dog == "Buddy the dog aged 7"


@dog.arguments("Champion")
class TestChampion:
    def test_with_dog(self, dog):
        assert dog == "Champion the dog aged 69"

    def test_with_default_owner(self, owner, dog):
        assert owner == "John Doe, owner of Champion the dog aged 69"
        assert dog == "Champion the dog aged 69"

    @owner.arguments("John Travolta")
    def test_with_named_owner(self, owner):
        assert owner == "John Travolta, owner of Champion the dog aged 69"

The code for the decorator:

import pytest
from dataclasses import dataclass
from functools import wraps
from inspect import signature, Parameter, isgeneratorfunction, iscoroutinefunction, isasyncgenfunction
from itertools import zip_longest, chain


_NOTHING = object()


def _omittable_parentheses_decorator(decorator):
    @wraps(decorator)
    def wrapper(*args, **kwargs):
        if not kwargs and len(args) == 1 and callable(args[0]):
            return decorator()(args[0])
        else:
            return decorator(*args, **kwargs)
    return wrapper


@dataclass
class _ArgsKwargs:
    args: ...
    kwargs: ...

    def __repr__(self):
        return ", ".join(chain(
               (repr(v) for v in self.args), 
               (f"{k}={v!r}" for k, v in self.kwargs.items())))


def _flatten_arguments(sig, args, kwargs):
    assert len(sig.parameters) == len(args) + len(kwargs)
    for name, arg in zip_longest(sig.parameters, args, fillvalue=_NOTHING):
        yield arg if arg is not _NOTHING else kwargs[name]


def _get_actual_args_kwargs(sig, args, kwargs):
    request = kwargs["request"]
    try:
        request_args, request_kwargs = request.param.args, request.param.kwargs
    except AttributeError:
        request_args, request_kwargs = (), {}
    return tuple(_flatten_arguments(sig, args, kwargs)) + request_args, request_kwargs


@_omittable_parentheses_decorator
def fixture_taking_arguments(*pytest_fixture_args, **pytest_fixture_kwargs):
    def decorator(func):
        original_signature = signature(func)

        def new_parameters():
            for param in original_signature.parameters.values():
                if param.kind == Parameter.POSITIONAL_ONLY:
                    yield param.replace(kind=Parameter.POSITIONAL_OR_KEYWORD)

        new_signature = original_signature.replace(parameters=list(new_parameters()))

        if "request" not in new_signature.parameters:
            raise AttributeError("Target function must have positional-only argument `request`")

        is_async_generator = isasyncgenfunction(func)
        is_async = is_async_generator or iscoroutinefunction(func)
        is_generator = isgeneratorfunction(func)

        if is_async:
            @wraps(func)
            async def wrapper(*args, **kwargs):
                args, kwargs = _get_actual_args_kwargs(new_signature, args, kwargs)
                if is_async_generator:
                    async for result in func(*args, **kwargs):
                        yield result
                else:
                    yield await func(*args, **kwargs)
        else:
            @wraps(func)
            def wrapper(*args, **kwargs):
                args, kwargs = _get_actual_args_kwargs(new_signature, args, kwargs)
                if is_generator:
                    yield from func(*args, **kwargs)
                else:
                    yield func(*args, **kwargs)

        wrapper.__signature__ = new_signature
        fixture = pytest.fixture(*pytest_fixture_args, **pytest_fixture_kwargs)(wrapper)
        fixture_name = pytest_fixture_kwargs.get("name", fixture.__name__)

        def parametrizer(*args, **kwargs):
            return pytest.mark.parametrize(fixture_name, [_ArgsKwargs(args, kwargs)], indirect=True)

        fixture.arguments = parametrizer

        return fixture
    return decorator

pytest无法导入模块,而python可以

问题:pytest无法导入模块,而python可以

我正在使用Python开发包。我使用virtualenv。我在virtualenv的.pth路径中将模块的根目录设置为路径,以便在开发代码并进行测试时可以导入软件包的模块(问题1:这是一个好方法吗?)。这工作正常(这是一个示例,这是我想要的行为):

(VEnvTestRc) zz@zz:~/Desktop/GitFolders/rc$ python
Python 2.7.12 (default, Jul  1 2016, 15:12:24) 
[GCC 5.4.0 20160609] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from rc import ns
>>> exit()
(VEnvTestRc) zz@zz:~/Desktop/GitFolders/rc$ python tests/test_ns.py 
issued command: echo hello
command output: hello

但是,如果我尝试使用PyTest,则会收到一些导入错误消息:

(VEnvTestRc) zz@zz:~/Desktop/GitFolders/rc$ pytest
=========================================== test session starts ============================================
platform linux2 -- Python 2.7.12, pytest-3.0.5, py-1.4.31, pluggy-0.4.0
rootdir: /home/zz/Desktop/GitFolders/rc, inifile: 
collected 0 items / 1 errors 

================================================== ERRORS ==================================================
________________________________ ERROR collecting tests/test_ns.py ________________________________
ImportError while importing test module '/home/zz/Desktop/GitFolders/rc/tests/test_ns.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
tests/test_ns.py:2: in <module>
    from rc import ns
E   ImportError: cannot import name ns
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Interrupted: 1 errors during collection !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
========================================= 1 error in 0.09 seconds ==========================================
(VEnvTestRc) zz@zz:~/Desktop/GitFolders/rc$ which pytest
/home/zz/Desktop/VirtualEnvs/VEnvTestRc/bin/pytest

我有点疑惑,它看起来像是一个导入错误,但是Python很好,所以为什么PyTest特有问题?对原因/补救措施有什么建议(问题2)?我用谷歌搜索并堆栈溢出了PyTest的“ ImportError:无法导入”错误,但是我得到的结果与缺少python路径有关,并且对此有补救措施,这似乎不是这里的问题。有什么建议?

I am working on a package in Python. I use virtualenv. I set the path to the root of the module in a .pth path in my virtualenv, so that I can import modules of the package while developing the code and do testing (Question 1: is it a good way to do?). This works fine (here is an example, this is the behavior I want):

(VEnvTestRc) zz@zz:~/Desktop/GitFolders/rc$ python
Python 2.7.12 (default, Jul  1 2016, 15:12:24) 
[GCC 5.4.0 20160609] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from rc import ns
>>> exit()
(VEnvTestRc) zz@zz:~/Desktop/GitFolders/rc$ python tests/test_ns.py 
issued command: echo hello
command output: hello

However, if I try to use PyTest, I get some import error messages:

(VEnvTestRc) zz@zz:~/Desktop/GitFolders/rc$ pytest
=========================================== test session starts ============================================
platform linux2 -- Python 2.7.12, pytest-3.0.5, py-1.4.31, pluggy-0.4.0
rootdir: /home/zz/Desktop/GitFolders/rc, inifile: 
collected 0 items / 1 errors 

================================================== ERRORS ==================================================
________________________________ ERROR collecting tests/test_ns.py ________________________________
ImportError while importing test module '/home/zz/Desktop/GitFolders/rc/tests/test_ns.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
tests/test_ns.py:2: in <module>
    from rc import ns
E   ImportError: cannot import name ns
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Interrupted: 1 errors during collection !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
========================================= 1 error in 0.09 seconds ==========================================
(VEnvTestRc) zz@zz:~/Desktop/GitFolders/rc$ which pytest
/home/zz/Desktop/VirtualEnvs/VEnvTestRc/bin/pytest

I am a bit puzzled, it looks like this indicates an import error, but Python does it fine so why is there a problem specifically with PyTest? Any suggestion to the reason / remedy (Question 2)? I googled and stack-overflowed the ‘ImportError: cannot import’ error for PyTest, but the hits I got were related to missing python path and remedy to this, which does not seem to be the problem here. Any suggestions?


回答 0

找到了答案:

__init__.py如果计划使用pytest,请勿将文件放在包含TESTS的文件夹中。我有一个这样的文件,删除它可以解决问题。

这实际上是在pytest“ ImportError:没有名为YadaYadaYada的模块”PATH问题的第二个答案的注释中掩埋的,所以我没有看到它,希望它在这里能够引起更多的关注。

Found the answer:

DO NOT put a __init__.py file in a folder containing TESTS if you plan on using pytest. I had one such file, deleting it solved the problem.

This was actually buried in the comments to the second answer of PATH issue with pytest ‘ImportError: No module named YadaYadaYada’ so I did not see it, hope it gets more visibility here.


回答 1

我不能说我理解为什么会这样,但是我遇到了同样的问题,如果运行,测试也可以正常工作python -m pytest

我在virtualenv中,pytest也可以在全球范围内使用:

(proj)tom@neon ~/dev/proj$ type -a python
python is /home/tom/.virtualenvs/proj/bin/python
python is /usr/bin/python

(proj)tom@neon ~/dev/proj$ python -V
Python 3.5.2

(proj)tom@neon ~/dev/proj$ type -a pytest
pytest is /home/tom/.virtualenvs/proj/bin/pytest
pytest is /usr/bin/pytest

(proj)tom@neon ~/dev/proj$ pytest --version
This is pytest version 3.5.0, imported from /home/tom/.virtualenvs/proj/lib/python3.5/site-packages/pytest.py

I can’t say I understand why this works, but I had the same problem and the tests work fine if I run python -m pytest.

I’m in a virtualenv, with pytest also available globally:

(proj)tom@neon ~/dev/proj$ type -a python
python is /home/tom/.virtualenvs/proj/bin/python
python is /usr/bin/python

(proj)tom@neon ~/dev/proj$ python -V
Python 3.5.2

(proj)tom@neon ~/dev/proj$ type -a pytest
pytest is /home/tom/.virtualenvs/proj/bin/pytest
pytest is /usr/bin/pytest

(proj)tom@neon ~/dev/proj$ pytest --version
This is pytest version 3.5.0, imported from /home/tom/.virtualenvs/proj/lib/python3.5/site-packages/pytest.py

回答 2

我只是通过删除项目根目录中的__init__.py来解决此问题:

.
├── __init__.py <--- removed
├── models
   ├── __init__.py
   ├── address.py
   ├── appointment.py
   └── client.py
├── requirements.txt
├── setup.cfg
├── tests
   ├── __init__.py
   ├── models
      ├── __init__.py
      ├── appointment_test.py
      └── client_test.py
   └── other_test.py
└── script.py

I just solved this by removing the __init__.py in my project root:

.
├── __init__.py <--- removed
├── models
│   ├── __init__.py
│   ├── address.py
│   ├── appointment.py
│   └── client.py
├── requirements.txt
├── setup.cfg
├── tests
│   ├── __init__.py
│   ├── models
│   │   ├── __init__.py
│   │   ├── appointment_test.py
│   │   └── client_test.py
│   └── other_test.py
└── script.py

回答 3

我遇到了同样的问题,但是由于上述原因之外的另一个原因:

我在全局安装了py.test,而软件包则安装在虚拟环境中。

解决方案是pytest在虚拟环境中安装。(如果您的shell像Bash一样哈希可执行文件,请使用hash -r,或使用的完整路径py.test

I had the same problem but for another reason than the ones mentioned:

I had py.test installed globally, while the packages were installed in a virtual environment.

The solution was to install pytest in the virtual environment. (In case your shell hashes executables, as Bash does, use hash -r, or use the full path to py.test)


回答 4

如果您有一个tests.py文件和一个带有的测试文件夹,则会发生此问题tests/__init__.py

在收集过程中,pytest会找到文件夹,但是当尝试从文件夹中导入测试文件时,tests.py文件会导致导入问题。

要解决此问题,只需删除tests.py文件,然后将所有测试放在tests/文件夹中即可。

对于您的特定情况,修复将精确地是:

  • 删除文件 /home/zz/Desktop/GitFolders/rc/tests.py
  • 确保/home/zz/Desktop/GitFolders/rc/tests/__init__.py存在

This problem will happen if you have a tests.py file and a tests folder with tests/__init__.py.

During the collection pytest finds the folder, but when it tries to import the test files from the folder, tests.py file will cause the import problem.

To fix simply remove the tests.py file and put all your tests inside the tests/ folder.

For your specific case the fix will be precisely:

  • Remove the file /home/zz/Desktop/GitFolders/rc/tests.py
  • Make sure /home/zz/Desktop/GitFolders/rc/tests/__init__.py is present

回答 5

我有一个类似的问题,完全相同的错误,但原因不同。我正在运行测试代码,但是使用的是模块的旧版本。在我的代码的先前版本中,一个类存在,而另一个不存在。更新代码后,我应该运行以下命令进行安装。

sudo pip install ./ --upgrade

安装更新的模块后,运行pytest会产生正确的结果(因为我使用的是正确的代码库)。

I had a similar issue, exact same error, but different cause. I was running the test code just fine, but against an old version of the module. In the previous version of my code one class existed, while the other did not. After updating my code, I should have run the following to install it.

sudo pip install ./ --upgrade

Upon installing the updated module running pytest produced the correct results (because i was using the correct code base).


回答 6

在我的情况下,发生导入错误是因为程序包指向另一个具有相同名称的程序包/目录,并且其路径在我实际想要的文件夹上方一层。我认为这也解释了为什么有些人需要删除_ init _.py而另一些人需要重新添加。

我只是在控制台和脚本中print(the_root_package.__path__)(在之后import the_root_package)放置以比较差异pythonpytest

底线:执行此操作时python,导入的包可能与运行时的包不同pytest

In my case, the import error occurred because the package is pointing to another package/directory with the same name and its path is one level above the folder I actually wanted. I think this also explains why some people need to remove _ init _.py while others need to add back.

I just put print(the_root_package.__path__) (after import the_root_package) in both python console and pytest scripts to compare the difference

BOTTOM LINE: When you do python, the package you import may be different from the package when you run pytest.


回答 7

将软件包安装到您的虚拟环境中。
然后启动一个新的Shell并再次获取您的虚拟环境。

Install the packages into Your virtual environment.
Then start a new shell and source Your virtual environment again.


回答 8

上面的答案对我不起作用。我只是通过将未找到的模块的绝对路径附加到(您的测试模块)的sys.path顶部来解决它test_xxx.py,例如:

import sys
sys.path.append('path')

The answer above not work for me. I just solved it by appending the absolute path of the module which not found to the sys.path at top of the test_xxx.py (your test module), like:

import sys
sys.path.append('path')

回答 9

如果它与最初在python 2.7中开发,现在迁移到python 3.x的python代码有关,则该问题可能与导入问题有关。

例如,从base同一目录中的文件导入对象时,它将在python 2.x中工作:

from base import MyClass

在python 3.x中,您应该替换为base完整路径,否则.base 将导致上述问题。所以尝试:

from .base import MyClass

If it is related to python code that was originally developed in python 2.7 and now migrated into python 3.x than the problem is probably related to an import issue.

e.g. when importing an object from a file: base that is located in the same directory this will work in python 2.x:

from base import MyClass

in python 3.x you should replace with base full path or .base not doing so will cause the above problem. so try:

from .base import MyClass

回答 10

我今天遇到了这个问题,并通过python -m pytest从项目目录的根目录进行调用来解决了。

pytest从同一位置拨打电话仍然会引起问题。

我的项目目录组织为:

api/
 - server/
  - tests/
      - test_routes.py
  - routes/
      - routes.py
 - app.py

该模块routes以我的test_routes.py身份导入:from server.routes.routes import Routes

希望有帮助!

I was experiencing this issue today and solved it by calling python -m pytest from the root of my project directory.

Calling pytest from the same location still caused issues.

My Project dir is organized as:

api/
 - server/
  - tests/
      - test_routes.py
  - routes/
      - routes.py
 - app.py

The module routes was imported in my test_routes.py as: from server.routes.routes import Routes

Hope that helps!


回答 11

另一个特殊情况:

我在使用毒药时遇到了问题。因此,我的程序运行良好,但是通过Tox进行的单元测试一直在抱怨。安装软件包(程序需要)之后,您需要另外在tox.ini中指定单元测试中使用的软件包。

[testenv]
deps =
    package1
    package2 
...

Another special case:

I had the problem using tox. So my program ran fine, but unittests via tox kept complaining. After installing packages (needed for the program) you need to additionally specify the packages used in the unittests in the tox.ini

[testenv]
deps =
    package1
    package2 
...

回答 12

我正在使用VSCode来获得它。我有一个conda环境。我不认为VScode python扩展可以看到我所做的更新。

python c:\Users\brig\.vscode\extensions\ms-python.python-2019.9.34911\pythonFiles\testing_tools\run_adapter.py discover pytest -- -s --cache-clear test
Test Discovery failed:

我必须跑步 pip install ./ --upgrade

I was getting this using VSCode. I have a conda environment. I don’t think the VScode python extension could see the updates I was making.

python c:\Users\brig\.vscode\extensions\ms-python.python-2019.9.34911\pythonFiles\testing_tools\run_adapter.py discover pytest -- -s --cache-clear test
Test Discovery failed:

I had to run pip install ./ --upgrade


回答 13

编辑您的conftest.py并添加以下代码行:

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

如果尝试通过终端运行测试用例,请使用以下示例:

python -m pytest test_some_step_file_steps.py --html=HTML_step_file_output.html --self-contained-html

Edit your conftest.py and add following lines of code:

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

And if trying to run the test case through terminal, use following ex:

python -m pytest test_some_step_file_steps.py --html=HTML_step_file_output.html --self-contained-html

回答 14

Python的导入系统又获得了一次巨大的胜利。我认为,没有达成共识的原因是,有效的方法可能取决于您的环境以及在其上使用的工具。

我正在VS Code中使用它,它是在conda环境下Windows的Python 3.8中的测试资源管理器中使用的。

我要工作的设置是:

mypkg/
    __init__.py
    app.py
    view.py
tests/
    test_app.py
    test_view.py

在这种设置下,智能感知将起作用,测试发现也将起作用。

请注意,我最初试图以下,推荐这里

src/
    mypkg/
        __init__.py
        app.py
        view.py
tests/
    test_app.py
    test_view.py

我找不到从VS Code上src使它起作用的方法,因为该文件夹使导入系统无所适从。我可以想象有一种方法可以从命令行运行它。作为一种相对较新的Python编程转换,它带给我怀旧的使用COM的感觉,但是却没有那么有趣。

Yet another massive win for Python’s import system. I think the reason there is no consensus is that what works probably depends on your environment and the tools you are using on top of it.

I’m using this from VS Code, in the test explorer under Windows in a conda environment, Python 3.8.

The setup I have got to work is:

mypkg/
    __init__.py
    app.py
    view.py
tests/
    test_app.py
    test_view.py

Under this setup intellisense works and so does test discovery.

Note that I originally tried the following, as recommended here.

src/
    mypkg/
        __init__.py
        app.py
        view.py
tests/
    test_app.py
    test_view.py

I could find no way of getting this to work from VS Code because the src folder just blew the mind of the import system. I can imagine there is a way of getting this to work from the command line. As a relatively new convert to Python programming it gives me a nostalgic feeling of working with COM, but being slightly less fun.


回答 15

如果不使用虚拟环境,我为此付出的2美分:pytest会偶然失败。有时它会工作,有时则不会。

因此,解决方案是:

  • 用pip卸载删除pytest
  • 创建您的venv
  • 激活你的venv
  • pip以可编辑模式安装项目路径,因此pytest会将其视为模块(否则,pytest将找不到您的内部导入)。您将需要一个setup.py文件
  • 安装您的软件包,包括pytest
  • 最后,运行测试

使用Windows PowerShell的代码:

pip uninstall pytest
python.exe -m venv my_env
.\my_env\Scripts\activate
(my_env) pip install -e .
(my_env) pip install pytest pytest-html pandas numpy

然后最后

(my_env) pytest --html="my_testing_report.html"

用于pip install -e的setup.py示例:

import setuptools

setuptools.setup(
    name='my_package',
    version='devel',
    author='erickfis',
    author_email='erickfis@gmail.com',
    description='My package',
    long_description='My gooood package',
    packages=setuptools.find_packages(),
    classifiers=[
        'Programming Language :: Python :: 3',
        'Operating System :: OS Independent',
    ],
    include_package_data=True
)

My 2 cents on this: pytest will fail at chance if you are not using virtual environments. Sometimes it will just work, sometimes not.

Therefore, the solution is:

  • remove pytest with pip uninstall
  • create your venv
  • activate your venv
  • pip install your project path in editable mode, so it will be treated by pytest as a module (otherwise, pytest wont find your internal imports). You will need a setup.py file for that
  • install your packages, including pytest
  • finally, run your tests

The code, using windows PowerShell:

pip uninstall pytest
python.exe -m venv my_env
.\my_env\Scripts\activate
(my_env) pip install -e .
(my_env) pip install pytest pytest-html pandas numpy

Then finally

(my_env) pytest --html="my_testing_report.html"

An example of setup.py, for pip install -e:

import setuptools

setuptools.setup(
    name='my_package',
    version='devel',
    author='erickfis',
    author_email='erickfis@gmail.com',
    description='My package',
    long_description='My gooood package',
    packages=setuptools.find_packages(),
    classifiers=[
        'Programming Language :: Python :: 3',
        'Operating System :: OS Independent',
    ],
    include_package_data=True
)

回答 16

我不同意这些帖子,说您必须删除所有__init__.py文件。您必须要做的是更改sys.path

sys.path正常运行代码的情况下,在您进行打印的地方进行实验。然后sys.path在通过pytest运行代码的同时进行打印。我认为您会发现这两条路径之间存在差异,因此pytest为什么会中断。

要解决此问题,请将第一个实验的路径插入第二个实验的第0个索引。

我们'/usr/exampleUser/Documents/foo'是第一个元素print(sys.path)的实验1。

以下是应该解决您问题的代码:

import sys sys.path[0] = '/usr/exampleUser/Documents/foo'

在实际导入语句之前,将此内容放在文件顶部。

资料来源:我本人正在处理,上述过程解决了它。

I disagree with the posts saying that you must remove any __init__.py files. What you must instead do is alter the sys.path.

Run an experiment where you print sys.path when running the code normally. Then print sys.path while running the code via pytest. I think you will find that there is a difference between these two paths, hence why pytest breaks.

To fix this, insert the path from the first experiment at the 0th index of the second.

Let '/usr/exampleUser/Documents/foo' be the first element of print(sys.path) for experiment 1.

Below is code that should fix your issue:

import sys sys.path[0] = '/usr/exampleUser/Documents/foo'

Put this at the top of your file, before your actual import statement.

Source: I was dealing with this myself and the above process solved it.


回答 17

一切都保持相同,只是增加了在根文件夹中的空白测试文件..解决它

这是调查结果,这个问题确实困扰了我一段时间。我的文件夹结构是

mathapp/
    - server.py  
    - configuration.py 
    - __init__.py 
    - static/ 
       - home.html  
tests/            
    - functional 
       - test_errors.py 
    - unit  
       - test_add.py

pytest会抱怨ModuleNotFoundError并给出提示-确保您的测试模块/软件包具有有效的Python名称。

我在与mathsapp和tests目录相同的级别引入了模拟测试文件。该文件不包含任何内容。现在pytest不会抱怨。

没有文件的结果

$ pytest
============================= test session starts =============================
platform win32 -- Python 3.8.2, pytest-5.4.2, py-1.8.1, pluggy-0.13.1
rootdir: C:\mak2006\workspace\0github\python-rest-app-cont
collected 1 item / 1 error

=================================== ERRORS ====================================
_______________ ERROR collecting tests/functional/test_func.py ________________
ImportError while importing test module 'C:\mainak\workspace\0github\python-rest-app-cont\tests\functional\test_func.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
tests\functional\test_func.py:4: in <module>
    from mathapp.service import sum
E   ModuleNotFoundError: No module named 'mathapp'
=========================== short test summary info ===========================
ERROR tests/functional/test_func.py
!!!!!!!!!!!!!!!!!!! Interrupted: 1 error during collection !!!!!!!!!!!!!!!!!!!!
============================== 1 error in 0.24s ===============================

结果与文件

$ pytest
============================= test session starts =============================
platform win32 -- Python 3.8.2, pytest-5.4.2, py-1.8.1, pluggy-0.13.1
rootdir: C:\mak2006\workspace\0github\python-rest-app-cont
collected 2 items

tests\functional\test_func.py .                                          [ 50%]
tests\unit\test_unit.py .                                                [100%]

============================== 2 passed in 0.11s ==============================

Kept everything same and just added a blank test file at the root folder .. Solved it

Here are the findings, this problem really bugged me for a while. My folder structure was

mathapp/
    - server.py  
    - configuration.py 
    - __init__.py 
    - static/ 
       - home.html  
tests/            
    - functional 
       - test_errors.py 
    - unit  
       - test_add.py

and pytest would complain with the ModuleNotFoundError and gives the HINT – make sure your test modules/packages have valid Python names.

I introduced a mock test file at the same level as mathsapp and tests directory. The file contained nothing. Now pytest does not complain.

Result without the file

$ pytest
============================= test session starts =============================
platform win32 -- Python 3.8.2, pytest-5.4.2, py-1.8.1, pluggy-0.13.1
rootdir: C:\mak2006\workspace\0github\python-rest-app-cont
collected 1 item / 1 error

=================================== ERRORS ====================================
_______________ ERROR collecting tests/functional/test_func.py ________________
ImportError while importing test module 'C:\mainak\workspace\0github\python-rest-app-cont\tests\functional\test_func.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
tests\functional\test_func.py:4: in <module>
    from mathapp.service import sum
E   ModuleNotFoundError: No module named 'mathapp'
=========================== short test summary info ===========================
ERROR tests/functional/test_func.py
!!!!!!!!!!!!!!!!!!! Interrupted: 1 error during collection !!!!!!!!!!!!!!!!!!!!
============================== 1 error in 0.24s ===============================

Results with the file

$ pytest
============================= test session starts =============================
platform win32 -- Python 3.8.2, pytest-5.4.2, py-1.8.1, pluggy-0.13.1
rootdir: C:\mak2006\workspace\0github\python-rest-app-cont
collected 2 items

tests\functional\test_func.py .                                          [ 50%]
tests\unit\test_unit.py .                                                [100%]

============================== 2 passed in 0.11s ==============================

回答 18

我通过PYTHONPATH为运行测试的特定配置设置环境变量解决了我的问题。

在PyCharm上查看测试文件时:

  1. Ctrl+ Shift+A
  2. 类型 Edit Configurations
  3. PYTHONPATH在环境>环境变量下设置。

I solved my problem by setting the PYTHONPATH in Environment Variables for the specific configuration I’m running my tests with.

While you’re viewing the test file on PyCharm:

  1. Ctrl+Shift+A
  2. Type Edit Configurations
  3. Set your PYTHONPATH under Environment > Environment variables.

回答 19

只需将一个空conftest.py文件放在项目根目录中,因为当pytest发现conftest.py时,它将修改sys.path以便可以从conftest模块中导入内容。常规目录结构可以是:

Root
├── conftest.py
├── module1
   ├── __init__.py
   └── sample.py
└── tests
    └── test_sample.py

Simply put an empty conftest.py file in the project root directory, because when pytest discovers a conftest.py, it modifies sys.path so it can import stuff from the conftest module. A general directory structure can be:

Root
├── conftest.py
├── module1
│   ├── __init__.py
│   └── sample.py
└── tests
    └── test_sample.py

回答 20

有一个类似的问题,当我__init__.py在tests目录下添加文件时,它可以工作。

Had a similar issue and it worked when I added __init__.py file under tests directory.


回答 21

可能是因为Pytest并未将Python包作为Python模块读取(可能是由于路径问题)。尝试更改pytest脚本的目录或将模块显式添加到PYTHONPATH。

也可能是您的计算机上安装了两个版本的Python。检查您的Python源是否有pytest和您运行的python shell。如果它们不同(例如,Python 2 vs 3),请使用source activate来确保您正在为安装模块的相同python运行pytest。

It could be that Pytest is not reading the package as a Python module while Python is (likely due to path issues). Try changing the directory of the pytest script or adding the module explicitly to your PYTHONPATH.

Or it could be that you have two versions of Python installed on your machine. Check your Python source for pytest and for the python shell that you run. If they are different (i.e. Python 2 vs 3), use source activate to make sure that you are running the pytest installed for the same python that the module is installed in.


回答 22

对于任何尝试了一切但仍然出错的人,我都有解决方法。

安装pytest文件夹中,转到pytest-env文件夹。

打开 pyvenv.cfg文件。

在文件中,将include-system-site-packagesfalse更改为true

home = /usr/bin
include-system-site-packages = true
version = 3.6.6

希望它能起作用。不要忘记投票。

For anyone who tried everything and still getting error,I have a work around.

In the folder where pytest is installed,go to pytest-env folder.

Open pyvenv.cfg file.

In the file change include-system-site-packages from false to true.

home = /usr/bin
include-system-site-packages = true
version = 3.6.6

Hope it works .Don’t forget to up vote.


回答 23

如果您已经有.pyc文件,请尝试将其删除。

今天,我遇到了这个问题,这是发生了什么:

首先,我在Mac中运行pytest(这将生成pyc文件),然后启动安装了项目目录的docker容器(操作系统为alpine),然后尝试在容器中运行pytest时,发生ImportError。清除所有pyc文件后,再也没有错误。

希望这会有所帮助。

If you already have .pyc files, try to delete them.

Today, I encounter this problem, here is what happend:

first I run pytest in mac (this will generate pyc files) then I launch a docker container (the os is alpine), with project dir mounted, and then when I try to run pytest in container, ImportError happens. after cleaning all pyc files, no error any more.

Hope this may be helpful.


回答 24

如果您的文件夹中需要一个init .py文件,请制作该文件夹的副本,然后删除其中的init .py来运行您的测试,该测试适用于本地项目。如果需要定期运行测试,请查看是否可以将init .py移到单独的文件中。

if you need a init.py file in your folder make a copy of the folder and delete init.py in that one to run your tests it works for local projects. If you need to run test regularly see if you can move your init.py to a separate file.


回答 25

[解决]在直接进入删除/添加的解决方案之前__init__.py,我们可能还想看一下如何在您的类中完成导入。实际上,我只是__init__.py想着这可能是问题的原因,所以迷失了一天。)但是,那确实很有用。

就我而言,这是将类从一个python类调用到另一个正在抛出的python类的错误方式 ImportError。修复了类/模块的调用方式,它就像魅力一样工作。希望这对其他人也有帮助。

是的,对于类似的错误,根据代码的编写方式,我们可能有不同的解决方案。最好花更多时间进行自我调试。获得的经验:)编码愉快!!!

[Resolved] Before directly jumping into the solution of removing/ adding __init__.py, we might also want to look at how the imports are been done in your classes. Actually, I lost a day playing around with just __init__.py thinking that might be the problem :) However, that was quite informative.

In my case, it was the wrong way of calling classes from one python class to another python class which was throwing ImportError. Fixed the way classes/ modules to be called and it worked like charm. Hope, this helps others as well.

And yes, for similar error, we might have different solutions depending on how the code is written. Better to spend more time on self debugging. Lesson Learnt :) Happy coding!!!


回答 26

就我而言,我在一个容器中工作,不幸的是pytest倾向于使用python2.7而不是我选择的python3解释器。

就我而言,这可行:

python3 -m pytest

我的文件夹结构

/
app/
-module1.py
-module2.py
-tests/
--test_module1.py
--test_module2.py
requirements.txt
README.md

In my case I am working in a container and unfortuantely pytest has the tendency to use python2.7 rather than my python3 interpreter of choice.

In my case this worked:

python3 -m pytest

My folder structure

/
app/
-module1.py
-module2.py
-tests/
--test_module1.py
--test_module2.py
requirements.txt
README.md

回答 27

我已经将所有测试放在一个tests文件夹中,并且得到了相同的错误。我通过在该文件夹中添加init .py 来解决此问题,如下所示:

.
|-- Pipfile
|-- Pipfile.lock
|-- README.md
|-- api
|-- app.py
|-- config.py
|-- migrations
|-- pull_request_template.md
|-- settings.py
`-- tests
    |-- __init__.py <------
    |-- conftest.py
    `-- test_sample.py

I had placed all my tests in a tests folder and was getting the same error. I solved this by adding an init.py in that folder like so:

.
|-- Pipfile
|-- Pipfile.lock
|-- README.md
|-- api
|-- app.py
|-- config.py
|-- migrations
|-- pull_request_template.md
|-- settings.py
`-- tests
    |-- __init__.py <------
    |-- conftest.py
    `-- test_sample.py

如何为带有测试的pytest类正确设置和拆卸?

问题:如何为带有测试的pytest类正确设置和拆卸?

我正在使用硒进行端到端测试,但无法获得使用方法setup_classteardown_class方法。

我需要在setup_class方法中设置浏览器,然后执行一堆定义为类方法的测试,最后退出teardown_class方法中的浏览器。

但是从逻辑上讲,这似乎是一个糟糕的解决方案,因为实际上我的测试不适用于类,而适用于对象。我self在每个测试方法中传递参数,因此可以访问对象的vars:

class TestClass:
  
    def setup_class(cls):
        pass
        
    def test_buttons(self, data):
        # self.$attribute can be used, but not cls.$attribute?  
        pass
        
    def test_buttons2(self, data):
        # self.$attribute can be used, but not cls.$attribute?
        pass
        
    def teardown_class(cls):
        pass
    

甚至为类创建浏览器实例似乎也不正确。应该为每个对象分别创建,对吗?

因此,我需要使用__init__and __del__方法代替setup_classand teardown_class

I am using selenium for end to end testing and I can’t get how to use setup_class and teardown_class methods.

I need to set up browser in setup_class method, then perform a bunch of tests defined as class methods and finally quit browser in teardown_class method.

But logically it seems like a bad solution, because in fact my tests will not work with class, but with object. I pass self param inside every test method, so I can access objects’ vars:

class TestClass:
  
    def setup_class(cls):
        pass
        
    def test_buttons(self, data):
        # self.$attribute can be used, but not cls.$attribute?  
        pass
        
    def test_buttons2(self, data):
        # self.$attribute can be used, but not cls.$attribute?
        pass
        
    def teardown_class(cls):
        pass
    

And it even seems not to be correct to create browser instance for class.. It should be created for every object separately, right?

So, I need to use __init__ and __del__ methods instead of setup_class and teardown_class?


回答 0

根据Fixture的完成/执行拆卸代码,当前设置和拆卸的最佳做法是使用yield而不是return

import pytest

@pytest.fixture()
def resource():
    print("setup")
    yield "resource"
    print("teardown")

class TestResource:
    def test_that_depends_on_resource(self, resource):
        print("testing {}".format(resource))

运行它会导致

$ py.test --capture=no pytest_yield.py
=== test session starts ===
platform darwin -- Python 2.7.10, pytest-3.0.2, py-1.4.31, pluggy-0.3.1
collected 1 items

pytest_yield.py setup
testing resource
.teardown


=== 1 passed in 0.01 seconds ===

编写拆卸代码的另一种方法是,将一个request-context对象接受到您的Fixture函数中,并request.addfinalizer使用执行一次或多次拆卸的函数调用其方法:

import pytest

@pytest.fixture()
def resource(request):
    print("setup")

    def teardown():
        print("teardown")
    request.addfinalizer(teardown)
    
    return "resource"

class TestResource:
    def test_that_depends_on_resource(self, resource):
        print("testing {}".format(resource))

According to Fixture finalization / executing teardown code, the current best practice for setup and teardown is to use yield instead of return:

import pytest

@pytest.fixture()
def resource():
    print("setup")
    yield "resource"
    print("teardown")

class TestResource:
    def test_that_depends_on_resource(self, resource):
        print("testing {}".format(resource))

Running it results in

$ py.test --capture=no pytest_yield.py
=== test session starts ===
platform darwin -- Python 2.7.10, pytest-3.0.2, py-1.4.31, pluggy-0.3.1
collected 1 items

pytest_yield.py setup
testing resource
.teardown


=== 1 passed in 0.01 seconds ===

Another way to write teardown code is by accepting a request-context object into your fixture function and calling its request.addfinalizer method with a function that performs the teardown one or multiple times:

import pytest

@pytest.fixture()
def resource(request):
    print("setup")

    def teardown():
        print("teardown")
    request.addfinalizer(teardown)
    
    return "resource"

class TestResource:
    def test_that_depends_on_resource(self, resource):
        print("testing {}".format(resource))

回答 1

当您编写“定义为类方法的测试”时,您是说类方法(将其作为第一个参数的方法)还是常规方法(将实例作为第一个参数的方法)?

由于您的示例使用self了测试方法,因此我假设是后者,因此您只需要使用setup_method

class Test:

    def setup_method(self, test_method):
        # configure self.attribute

    def teardown_method(self, test_method):
        # tear down self.attribute

    def test_buttons(self):
        # use self.attribute for test

测试方法实例传递给setup_methodteardown_method,但是如果您的设置/拆卸代码不需要了解测试上下文,则可以忽略该方法。可以在这里找到更多信息

我还建议您熟悉py.test的装置,因为它们是更强大的概念。

When you write “tests defined as class methods”, do you really mean class methods (methods which receive its class as first parameter) or just regular methods (methods which receive an instance as first parameter)?

Since your example uses self for the test methods I’m assuming the latter, so you just need to use setup_method instead:

class Test:

    def setup_method(self, test_method):
        # configure self.attribute

    def teardown_method(self, test_method):
        # tear down self.attribute

    def test_buttons(self):
        # use self.attribute for test

The test method instance is passed to setup_method and teardown_method, but can be ignored if your setup/teardown code doesn’t need to know the testing context. More information can be found here.

I also recommend that you familiarize yourself with py.test’s fixtures, as they are a more powerful concept.


回答 2

这可能会有所帮助http://docs.pytest.org/en/latest/xunit_setup.html

在测试套件中,我将测试用例分组。对于安装和拆卸,我需要该类中的所有测试用例,我使用setup_class(cls)teardown_class(cls)类方法。

对于每个测试用例的设置和拆卸,我使用setup_method(method)teardown_method(methods)

例:

lh = <got log handler from logger module>

class TestClass:
    @classmethod
    def setup_class(cls):
        lh.info("starting class: {} execution".format(cls.__name__))

    @classmethod
    def teardown_class(cls):
        lh.info("starting class: {} execution".format(cls.__name__))

    def setup_method(self, method):
        lh.info("starting execution of tc: {}".format(method.__name__))

    def teardown_method(self, method):
        lh.info("starting execution of tc: {}".format(method.__name__))

    def test_tc1(self):
        <tc_content>
        assert 

    def test_tc2(self):
        <tc_content>
        assert

现在,当我运行测试时,当TestClass执行开始时,它将记录何时开始执行,何时结束执行以及方法的详细信息。

您可以在相应位置添加其他设置和拆卸步骤。

希望能帮助到你!

This might help http://docs.pytest.org/en/latest/xunit_setup.html

In my test suite, I group my test cases into classes. For the setup and teardown I need for all the test cases in that class, I use the setup_class(cls) and teardown_class(cls) classmethods.

And for the setup and teardown I need for each of the test case, I use the setup_method(method) and teardown_method(methods)

Example:

lh = <got log handler from logger module>

class TestClass:
    @classmethod
    def setup_class(cls):
        lh.info("starting class: {} execution".format(cls.__name__))

    @classmethod
    def teardown_class(cls):
        lh.info("starting class: {} execution".format(cls.__name__))

    def setup_method(self, method):
        lh.info("starting execution of tc: {}".format(method.__name__))

    def teardown_method(self, method):
        lh.info("starting execution of tc: {}".format(method.__name__))

    def test_tc1(self):
        <tc_content>
        assert 

    def test_tc2(self):
        <tc_content>
        assert

Now when I run my tests, when the TestClass execution is starting, it logs the details for when it is beginning execution, when it is ending execution and same for the methods..

You can add up other setup and teardown steps you might have in the respective locations.

Hope it helps!


回答 3

正如@Bruno所建议的那样,使用pytest固定装置是另一种解决方案,可用于两个测试类甚至是简单的测试函数。这是测试python2.7函数的示例

import pytest

@pytest.fixture(scope='function')
def some_resource(request):
    stuff_i_setup = ["I setup"]

    def some_teardown():
        stuff_i_setup[0] += " ... but now I'm torn down..."
        print stuff_i_setup[0]
    request.addfinalizer(some_teardown)

    return stuff_i_setup[0]

def test_1_that_needs_resource(some_resource):
    print some_resource + "... and now I'm testing things..."

所以,跑步 test_1...生成:

I setup... and now I'm testing things...
I setup ... but now I'm torn down...

该通知stuff_i_setup是在夹具中引用,使该对象是setuptorn down为测试它与交互。您可以想象这对于持久性对象(例如假设的数据库或某些连接)很有用,必须在每次测试运行之前清除这些持久性对象以使它们隔离。

As @Bruno suggested, using pytest fixtures is another solution that is accessible for both test classes or even just simple test functions. Here’s an example testing python2.7 functions:

import pytest

@pytest.fixture(scope='function')
def some_resource(request):
    stuff_i_setup = ["I setup"]

    def some_teardown():
        stuff_i_setup[0] += " ... but now I'm torn down..."
        print stuff_i_setup[0]
    request.addfinalizer(some_teardown)

    return stuff_i_setup[0]

def test_1_that_needs_resource(some_resource):
    print some_resource + "... and now I'm testing things..."

So, running test_1... produces:

I setup... and now I'm testing things...
I setup ... but now I'm torn down...

Notice that stuff_i_setup is referenced in the fixture, allowing that object to be setup and torn down for the test it’s interacting with. You can imagine this could be useful for a persistent object, such as a hypothetical database or some connection, that must be cleared before each test runs to keep them isolated.


回答 4

添加@classmethod装饰器后,您的代码应该可以按预期工作。

@classmethod 
def setup_class(cls):
    "Runs once per class"

@classmethod 
def teardown_class(cls):
    "Runs at end of class"

参见http://pythontesting.net/framework/pytest/pytest-xunit-style-fixtures/

Your code should work just as you expect it to if you add @classmethod decorators.

@classmethod 
def setup_class(cls):
    "Runs once per class"

@classmethod 
def teardown_class(cls):
    "Runs at end of class"

See http://pythontesting.net/framework/pytest/pytest-xunit-style-fixtures/


如何配置PyCharm以运行py.test测试?

问题:如何配置PyCharm以运行py.test测试?

我想开始为我的Python代码编写单元测试,而py.test框架听起来比Python捆绑的unittest更好。因此,我向项目添加了“ tests”目录,并向其添加了test_sample.py。现在,我想配置PyCharm以运行“ tests”目录中的所有测试。

据称,PyCharm 在其测试运行程序支持py.test。您应该能够创建运行/调试配置来运行测试,并且PyCharm据称具有专门用于py.test“创建配置”对话框。但这是他们关于该主题的文档的完整范围,我无法在任何地方找到这个所谓的对话框。

如果在“项目”工具窗口中右键单击目录,则应该看到“创建<名称>”菜单项,但是唯一以“创建”开头的菜单项是“创建运行配置”。好的,也许文档只是错误的,“创建运行配置”听起来确实很有希望。不幸的是,其子菜单中仅有的两项是“ C:\ mypath …中的Unittests”和“ C:\ mypath …中的Doctests”,都不适用-我既未使用unittest也未使用doctest。py.test没有菜单项。

如果我打开test_sample.py并在编辑器窗口中单击鼠标右键,则会得到承诺的“创建<名称>”菜单项:有“在test_sa中创建’Unittests …’…”,然后是“运行” “ test_sa中的单元测试…”和“调试test_sa中的单元测试…”。同样,这都是单元测试框架所特有的。py.test没有。

如果我确实尝试显示“ unittest”的菜单项,则会出现一个对话框,其中包含“名称”,“类型”,带有“文件夹”,“模式”,“脚本”和“类”的“测试”组框”和“函数”等。这听起来与为Python单元测试添加配置的对话框所记录的内容完全不同,而不像应该显示的“名称”和“运行测试”以及“关键字”选项一样在py.test对话框的配置中。对话框中没有任何内容可以切换我要添加的测试框架。

我在Windows上使用Python 3.1.3和pytest 2.0.3的PyCharm 1.5.2。我可以从命令行成功运行py.test测试,所以这不是像pytest未正确安装那样的简单操作。

如何配置PyCharm以运行py.test测试?

I want to start writing unit tests for my Python code, and the py.test framework sounds like a better bet than Python’s bundled unittest. So I added a “tests” directory to my project, and added test_sample.py to it. Now I want to configure PyCharm to run all the tests in my “tests” directory.

PyCharm allegedly supports py.test in its test runner. You’re supposed to be able to create a run/debug configuration to run your tests, and PyCharm allegedly has a “create configuration” dialog box specifically for py.test. But that’s the complete extent of their documentation on the subject, and I can’t find this alleged dialog box anywhere.

If I right-click the directory in the Project tool window, I’m supposed to see a “Create <name>” menu item, but the only menu item starting with “Create” is “Create Run Configuration”. Okay, maybe the documentation is just wrong, and “Create Run Configuration” does sound promising. Unfortunately, the only two items in its submenu are “Unittests in C:\mypath…” and “Doctests in C:\mypath…”, neither of which applies — I’m using neither unittest nor doctest. There is no menu item for py.test.

If I open my test_sample.py and right-click in the editor window, I do get the promised “Create <name>” menu items: there’s “Create ‘Unittests in test_sa…’…”, followed by “Run ‘Unittests in test_sa…'” and “Debug ‘Unittests in test_sa…'”. So again, it’s all specific to the unittest framework; nothing for py.test.

If I do try the menu items that say “unittest”, I get a dialog box with options for “Name”, “Type”, a “Tests” group box with “Folder” and “Pattern” and “Script” and “Class” and “Function”, etc. This sounds exactly like what’s documented as the dialog to add a configuration for Python Unit Test, and not like the “Name” and “Test to run” and “Keywords” options that are supposed to show up in the configuration for py.test dialog. There’s nothing inside the dialog to switch which test framework I’m adding.

I’m using PyCharm 1.5.2 on Windows with Python 3.1.3 and pytest 2.0.3. I can successfully run py.test on my tests from the command line, so it’s not something simple like pytest not being installed properly.

How do I configure PyCharm to run my py.test tests?


回答 0

请转到文件| 设置| 工具| Python Integrated Tools,并将默认测试运行程序更改为py.test。然后,您将获得py.test选项来创建测试,而不是unittest。

Please go to File | Settings | Tools | Python Integrated Tools and change the default test runner to py.test. Then you’ll get the py.test option to create tests instead of the unittest one.


回答 1

PyCharm 2017.3

  1. Preference -> Tools -> Python integrated Tools-选择py.testDefault test runner
  2. 如果您使用Django- Preference -> Languages&Frameworks -> Django勾选Do not use Django Test runner
  3. 从中清除所有以前存在的测试配置Run/Debug configuration,否则将使用这些较旧的配置运行测试。
  4. 要设置一些默认的其他参数,请更新py.test默认配置。 Run/Debug Configuration -> Defaults -> Python tests -> py.test -> Additional Arguments

PyCharm 2017.3

  1. Preference -> Tools -> Python integrated Tools – Choose py.test as Default test runner.
  2. If you use Django Preference -> Languages&Frameworks -> Django – Set tick on Do not use Django Test runner
  3. Clear all previously existing test configurations from Run/Debug configuration, otherwise tests will be run with those older configurations.
  4. To set some default additional arguments update py.test default configuration. Run/Debug Configuration -> Defaults -> Python tests -> py.test -> Additional Arguments

回答 2

我认为您需要使用工具栏上的“运行/调试配置”项。单击它并单击“编辑配置”(或使用菜单项“运行”->“编辑配置”)。在左侧窗格的“默认”部分中,有一个“ py.test”项,我认为这是您想要的。

我还发现该手册与此用户界面不匹配。希望我正确理解了这个问题,对您有所帮助。

I think you need to use the Run/Debug Configuration item on the toolbar. Click it and ‘Edit Configurations’ (or alternatively use the menu item Run->Edit Configurations). In the ‘Defaults’ section in the left pane there is a ‘py.test’ item which I think is what you want.

I also found that the manual didn’t match up to the UI for this. Hope I’ve understood the problem correctly and that helps.


回答 3

这是我使它与pytest 3.7.2(通过pip安装)和pycharms一起工作的方式2017.3

  1. edit configurations

  1. 添加一个新的运行配置并选择 py.test

  1. 在运行配置详细信息中,您需要将target= python和下面的未命名字段设置为tests。看起来这是您的测试文件夹的名称。不太确定强硬。我还建议使用该-s参数,以便在调试测试时,控制台将正常运行。没有参数pytest会捕获输出,并使调试控制台出错。

  1. 我的测试文件夹如下所示。这刚好在我的项目(my_project/tests)的根目录之下。

  1. 我的foobar_test.py文件:(无需导入):
def test_foobar():
    打印(“ Hello pytest”)
    断言为真
  1. 使用常规运行命令运行它

Here is how I made it work with pytest 3.7.2 (installed via pip) and pycharms 2017.3:

  1. Go to edit configurations

  1. Add a new run config and select py.test

  1. In the run config details, you need to set target=python and the unnamed field below to tests. It looks like this is the name of your test folder. Not too sure tough. I also recommend the -s argument so that if you debug your tests, the console will behave properly. Without the argument pytest captures the output and makes the debug console buggy.

  1. My tests folder looks like that. This is just below the root of my project (my_project/tests).

  1. My foobar_test.py file: (no imports needed):
def test_foobar():
    print("hello pytest")
    assert True
  1. Run it with the normal run command


回答 4

可以肯定地说,它的文献不多。从默认设置添加新配置后,您将可以运行“ / Applications / PyCharm CE.app/Contents/helpers/pycharm/pytestrunner.py”脚本。它没有记录,并且有自己的命令行参数构想。

您可以:

  1. 尝试玩一下,反转脚本,看看是否可以使py.test接受参数。它可能会起作用;上半个小时对我来说不是。
  2. 只需从控制台运行“ py.test * .py”即可。

奇怪的是,您很难找到任何讨论,因为JetBrains擅长用自己的页面轰炸Google算法。

It’s poorly documented to be sure. Once you get add a new configuration from defaults, you will be in the realm of running the “/Applications/PyCharm CE.app/Contents/helpers/pycharm/pytestrunner.py” script. It’s not documented and has its own ideas of command line arguments.

You can:

  1. Try to play around, reverse the script, and see if you can somehow get py.test to accept arguments. It might work; it didn’t in the first half hour for me.
  2. Just run “py.test *.py” from a console.

Oddly, you will find it hard to find any discussion as JetBrains does a good job of bombing Google algorithms with its own pages.


回答 5

当我遇到相同的问题并找到解决方案pycharm版本时找到此线程:2017.1.2转到“首选项”->“工具”->“ Python集成工具”,然后从右侧面板中将默认测试运行器设置为py.test解决我的问题

find this thread when I hit the same question and found the solution pycharm version:2017.1.2 go to “Preferences” -> “Tools” -> “Python Integrated Tools” and set the default test runner from right side panel as py.test solve my problem


回答 6

在pycharm 2019.2中,您可以简单地执行以下操作以运行所有测试:

  1. 运行>编辑配置>添加pytest
  2. 设置选项,如以下屏幕截图所示
  3. 单击调试(或使用快捷键Shift + Alt + F9运行pytest)

有关pytest与pycharm的更高集成,请参阅https://www.jetbrains.com/help/pycharm/pytest.html

In pycharm 2019.2, you can simply do this to run all tests:

  1. Run > Edit Configurations > Add pytest
  2. Set options as shown in following screenshot
  3. Click on Debug (or run pytest using e.g. hotkeys Shift+Alt+F9)

For a higher integration of pytest into pycharm, see https://www.jetbrains.com/help/pycharm/pytest.html


回答 7

我正在使用2018.2

我运行->编辑配置…然后单击模式对话框左上方的+。选择“ python测试”-> py.test然后给它一个名称,例如“使用py.test进行所有测试”

我选择“目标:模块名称”,然后将测试放在其中的模块(对我来说就是“测试”),或者将测试与代码混合在一起的地方放在所有代码所在的模块中。这让我不寒而栗。

我设置了Python解释器。

我将工作目录设置为项目目录。

I’m using 2018.2

I do Run -> Edit Configurations… Then click the + in the upper left of the modal dialog. Select “python tests” -> py.test Then I give it a name like “All test with py.test”

I select Target: module name and put in the module where my tests are (that is ‘tests’ for me) or the module where all my code is if my tests are mixed in with my code. This was tripping me up.

I set the Python interpreter.

I set the working directory to the project directory.


回答 8

使用特殊的Conda python安装程序,其中包括py.test的pip安装以及Specs插件的使用(选项–spec)(对于Rspec来说,就像不错的测试摘要语言一样),我必须这样做;

1.编辑默认的py.test以包含option = –spec,这意味着使用插件:https : //github.com/pchomik/pytest-spec

2.使用py.test创建新的测试配置。更改其python解释器以使用〜/ anaconda / envs /您选择的解释器,例如py27作为我的命名。

3.删除“ unittests”测试配置。

4,现在默认的测试配置是py.test,带有我可爱的Rspec样式输出。我喜欢它!谢谢大家!

ps Jetbrains关于运行/调试配置的文档在这里:https ://www.jetbrains.com/help/pycharm/2016.1/run-debug-configuration-py-test.html?search = py.test

With a special Conda python setup which included the pip install for py.test plus usage of the Specs addin (option –spec) (for Rspec like nice test summary language), I had to do ;

1.Edit the default py.test to include option= –spec , which means use the plugin: https://github.com/pchomik/pytest-spec

2.Create new test configuration, using py.test. Change its python interpreter to use ~/anaconda/envs/ your choice of interpreters, eg py27 for my namings.

3.Delete the ‘unittests’ test configuration.

4.Now the default test config is py.test with my lovely Rspec style outputs. I love it! Thank you everyone!

p.s. Jetbrains’ doc on run/debug configs is here: https://www.jetbrains.com/help/pycharm/2016.1/run-debug-configuration-py-test.html?search=py.test


回答 9

在2018.3中,它似乎会自动检测到我正在使用pytest,这很好,但是仍然不允许从项目的顶层运行。我必须分别pytest为每个tests目录运行。

但是,我发现我可以选择一种配置,然后手动对其进行编辑以在项目的根目录下运行,并且可以正常工作。我必须在“配置”下拉列表中手动选择它-不能右键单击“项目”窗格中的根文件夹。但是至少它允许我同时运行所有测试。

With 2018.3 it appears to automatically detect that I’m using pytest, which is nice, but it still doesn’t allow running from the top level of the project. I had to run pytest for each tests directory individually.

However, I found that I could choose one of the configurations and manually edit it to run at the root of the project and that this worked. I have to manually choose it in the Configurations drop-down – can’t right click on the root folder in the Project pane. But at least it allows me to run all tests at once.


回答 10

为您的项目启用Pytest

  1. 打开设置/首选项| 工具| 如选择测试框架中所述的Python Integrated Tools设置对话框。
  2. 默认测试运行器字段中,选择pytest
  3. 单击确定以保存设置。

Enable Pytest for you project

  1. Open the Settings/Preferences | Tools | Python Integrated Tools settings dialog as described in Choosing Your Testing Framework.
  2. In the Default test runner field select pytest.
  3. Click OK to save the settings.


pytest:断言几乎相等

问题:pytest:断言几乎相等

如何assert almost equal使用py.test处理浮点数而不求助于以下内容:

assert x - 0.00001 <= y <= x + 0.00001

更具体地说,了解一种精巧的解决方案以快速比较浮点对而不用拆开它们将很有用:

assert (1.32, 2.4) == i_return_tuple_of_two_floats()

How to do assert almost equal with py.test for floats without resorting to something like:

assert x - 0.00001 <= y <= x + 0.00001

More specifically it will be useful to know a neat solution for quickly compare pairs of float, without unpacking them:

assert (1.32, 2.4) == i_return_tuple_of_two_floats()

回答 0

我注意到这个问题专门询问了py.test。py.test 3.0包含一个approx()功能(很好,实际上是类),为此非常有用。

import pytest

assert 2.2 == pytest.approx(2.3)
# fails, default is ± 2.3e-06
assert 2.2 == pytest.approx(2.3, 0.1)
# passes

# also works the other way, in case you were worried:
assert pytest.approx(2.3, 0.1) == 2.2
# passes

该文档位于此处:https : //docs.pytest.org/en/latest/reference.html#pytest-approx

I noticed that this question specifically asked about py.test. py.test 3.0 includes an approx() function (well, really class) that is very useful for this purpose.

import pytest

assert 2.2 == pytest.approx(2.3)
# fails, default is ± 2.3e-06
assert 2.2 == pytest.approx(2.3, 0.1)
# passes

# also works the other way, in case you were worried:
assert pytest.approx(2.3, 0.1) == 2.2
# passes

The documentation is here: https://docs.pytest.org/en/latest/reference.html#pytest-approx


回答 1

您将必须为您指定“几乎”是什么:

assert abs(x-y) < 0.0001

适用于元组(或任何序列):

def almost_equal(x,y,threshold=0.0001):
  return abs(x-y) < threshold

assert all(map(almost_equal, zip((1.32, 2.4), i_return_tuple_of_two_floats())

You will have to specify what is “almost” for you:

assert abs(x-y) < 0.0001

to apply to tuples (or any sequence):

def almost_equal(x,y,threshold=0.0001):
  return abs(x-y) < threshold

assert all(map(almost_equal, zip((1.32, 2.4), i_return_tuple_of_two_floats())

回答 2

如果您可以访问NumPy,则它具有出色的浮点比较功能,已经可以与进行成对比较numpy.testing

然后,您可以执行以下操作:

numpy.testing.assert_allclose(i_return_tuple_of_two_floats(), (1.32, 2.4))

If you have access to NumPy it has great functions for floating point comparison that already do pairwise comparison with numpy.testing.

Then you can do something like:

numpy.testing.assert_allclose(i_return_tuple_of_two_floats(), (1.32, 2.4))

回答 3

就像是

assert round(x-y, 5) == 0

这就是单元测试

对于第二部分

assert all(round(x-y, 5) == 0 for x,y in zip((1.32, 2.4), i_return_tuple_of_two_floats()))

将其包装在函数中可能更好

def tuples_of_floats_are_almost_equal(X, Y):
    return all(round(x-y, 5) == 0 for x,y in zip(X, Y))

assert tuples_of_floats_are_almost_equal((1.32, 2.4), i_return_tuple_of_two_floats())

Something like

assert round(x-y, 5) == 0

That is what unittest does

For the second part

assert all(round(x-y, 5) == 0 for x,y in zip((1.32, 2.4), i_return_tuple_of_two_floats()))

Probably better to wrap that in a function

def tuples_of_floats_are_almost_equal(X, Y):
    return all(round(x-y, 5) == 0 for x,y in zip(X, Y))

assert tuples_of_floats_are_almost_equal((1.32, 2.4), i_return_tuple_of_two_floats())

回答 4

这些答案已经存在很长时间了,但是我认为最简单,也最易读的方法是使用unittest来处理很多不错的断言,而不将其用于测试结构。

获取断言,忽略其余的unittest.TestCase

(基于此答案

import unittest

assertions = unittest.TestCase('__init__')

做出一些断言

x = 0.00000001
assertions.assertAlmostEqual(x, 0)  # pass
assertions.assertEqual(x, 0)  # fail
# AssertionError: 1e-08 != 0

实施原始问题的自动拆箱测试

只需使用*即可解包您的返回值,而无需引入新名称。

i_return_tuple_of_two_floats = lambda: (1.32, 2.4)
assertions.assertAlmostEqual(*i_return_tuple_of_two_floats())  # fail
# AssertionError: 1.32 != 2.4 within 7 places

These answers have been around for a long time, but I think the easiest and also most readable way is to use unittest for it’s many nice assertions without using it for the testing structure.

Get assertions, ignore rest of unittest.TestCase

(based on this answer)

import unittest

assertions = unittest.TestCase('__init__')

Make some assertions

x = 0.00000001
assertions.assertAlmostEqual(x, 0)  # pass
assertions.assertEqual(x, 0)  # fail
# AssertionError: 1e-08 != 0

Implement original questions’ auto-unpacking test

Just use * to unpack your return value without needing to introduce new names.

i_return_tuple_of_two_floats = lambda: (1.32, 2.4)
assertions.assertAlmostEqual(*i_return_tuple_of_two_floats())  # fail
# AssertionError: 1.32 != 2.4 within 7 places

回答 5

如果您希望某些东西不仅适用于浮点数,还可以使用小数,例如可以使用python’s math.isclose

    # - rel_tol=0.01` is 1% difference tolerance.
    assert math.isclose(actual_value, expected_value, rel_tol=0.01)

文件-https: //docs.python.org/3/library/math.html#math.isclose

If you want something that works not only with floats but for example Decimals you can use python’s math.isclose:

    # - rel_tol=0.01` is 1% difference tolerance.
    assert math.isclose(actual_value, expected_value, rel_tol=0.01)

Docs – https://docs.python.org/3/library/math.html#math.isclose


回答 6

我会用鼻子工具。它可以与py.test运行程序一起很好地运行,并具有其他同样有用的断言-assert_dict_equal(),assert_list_equal()等。

from nose.tools import assert_almost_equals
assert_almost_equals(x, y, places=7) #default is 7 

I’d use nose.tools. It plays well with py.test runner and have other equally useful asserts – assert_dict_equal(), assert_list_equal(), etc.

from nose.tools import assert_almost_equals
assert_almost_equals(x, y, places=7) #default is 7 

有没有一种方法可以指定从文件运行哪些pytest测试?

问题:有没有一种方法可以指定从文件运行哪些pytest测试?

有没有办法选择pytest要从文件运行的测试?例如,一个foo.txt包含要执行的测试列表的文件:

tests_directory/foo.py::test_001
tests_directory/bar.py::test_some_other_test

或者,是否可以通过pytest从不同目录中选择多个测试,而这些测试的测试名称中没有相同的模式?

pytest -k <pattern> 允许使用单一模式。

一种选择是pytest.mark对每个测试使用a ,但是我的要求是对不同文件运行不同的测试组合。

有没有一种方法可以指定多个模式以及每个模式的测试文件名?

要么

有没有一种方法可以在文件中指定确切的测试路径,并将该文件作为输入输入pytest

要么

是否有可以用于此目的的挂钩函数?

Is there a way to select pytest tests to run from a file? For example, a file foo.txt containing a list of tests to be executed:

tests_directory/foo.py::test_001
tests_directory/bar.py::test_some_other_test

Or, is there a way to select multiple tests, having no common pattern in test name, from different directories with pytest?

pytest -k <pattern> allows a single pattern.

One option is to have a pytest.mark against each test, but my requirement is to run different combination of tests from different files.

Is there a way to specify multiple patterns and a test file name for each pattern?

Or

Is there a way to specify the exact test paths in a file and feed that file as an input to pytest?

Or

Is there a hook function that can be utilized for this purpose?


回答 0

您可以使用-koption运行具有不同模式的测试用例:

py.test tests_directory/foo.py tests_directory/bar.py -k 'test_001 or test_some_other_test'

这将运行名称为test_001test_some_other_test的测试用例从而取消选择其余的测试用例。

注意:这将选择任何以test_001或test_some_other_test开头的测试用例。例如,如果您有测试用例test_0012,则也将选择它。

You can use -k option to run test cases with different patterns:

py.test tests_directory/foo.py tests_directory/bar.py -k 'test_001 or test_some_other_test'

This will run test cases with name test_001 and test_some_other_test deselecting the rest of the test cases.

Note: This will select any test case starting with test_001 or test_some_other_test. For example, if you have test case test_0012 it will also be selected.


回答 1

指定测试/选择测试

Pytest支持从命令行运行和选择测试的几种方法。

在模块中运行测试

pytest test_mod.py

在目录中运行测试

pytest testing/

通过关键字表达式运行测试

pytest -k "MyClass and not method"

这将运行包含与给定字符串表达式匹配的名称的测试,其中可能包括使用文件名,类名和函数名作为变量的Python运算符。上面的示例将运行,TestMyClass.test_something但不会运行TestMyClass.test_method_simple

按节点ID运行测试

每个收集的测试都分配有一个唯一的nodeid名称,该名称由模块文件名后跟说明符(例如类名,函数名和参数化参数)组成,并用::字符分隔。

要在模块中运行特定的测试,请执行以下操作:

pytest test_mod.py::test_func

在命令行中指定测试方法的另一个示例:

pytest test_mod.py::TestClass::test_method

通过标记表达式运行测试

pytest -m slow

将运行用@pytest.mark.slow装饰器装饰的所有测试。

有关更多信息,请参见标记

从包运行测试

pytest --pyargs pkg.testing

这将导入pkg.testing并使用其文件系统位置来查找并运行测试。

来源:https//docs.pytest.org/en/latest/usage.html#specifying-tests-selecting-tests

Specifying tests / selecting tests

Pytest supports several ways to run and select tests from the command-line.

Run tests in a module

pytest test_mod.py

Run tests in a directory

pytest testing/

Run tests by keyword expressions

pytest -k "MyClass and not method"

This will run tests which contain names that match the given string expression, which can include Python operators that use filenames, class names and function names as variables. The example above will run TestMyClass.test_something but not TestMyClass.test_method_simple.

Run tests by node ids

Each collected test is assigned a unique nodeid which consist of the module filename followed by specifiers like class names, function names and parameters from parametrization, separated by :: characters.

To run a specific test within a module:

pytest test_mod.py::test_func

Another example specifying a test method in the command line:

pytest test_mod.py::TestClass::test_method

Run tests by marker expressions

pytest -m slow

Will run all tests which are decorated with the @pytest.mark.slow decorator.

For more information see marks.

Run tests from packages

pytest --pyargs pkg.testing

This will import pkg.testing and use its filesystem location to find and run tests from.

Source: https://docs.pytest.org/en/latest/usage.html#specifying-tests-selecting-tests


回答 2

我的答案提供了在不同情况下运行测试子集的方法。

运行项目中的所有测试

pytest

在单个目录中运行测试

要从一个目录运行所有测试,请将该目录用作以下参数 pytest

pytest tests/my-directory

在单个测试文件/模块中运行测试

要运行充满测试的文件,请列出文件,并将相对路径作为参数pytest

pytest tests/my-directory/test_demo.py

运行一个测试功能

要运行单个测试功能,请添加::和测试功能名称:

pytest -v tests/my-directory/test_demo.py::test_specific_function

-v 用于查看运行了哪个功能。

运行一个测试班

要只运行一个类,请像对函数和add一样进行操作::,然后将类名添加到file参数:

pytest -v tests/my-directory/test_demo.py::TestClassName

运行测试类的单一测试方法

如果您不想运行所有测试类,只需运行一个方法,只需添加另一个::方法名称即可:

pytest -v tests/my-directory/test_demo.py::TestClassName::test_specific_method

根据测试名称运行一组测试

-k选项使您可以传入表达式以运行具有由表达式指定为测试名称的子字符串的某些名称的测试。它可以使用,并没有创建复杂的表达式。

例如,要运行所有名称中带有_raises的函数:

pytest -v -k _raises

My answer provides a ways to run a subset of test in different scenarios.

Run all tests in a project

pytest

Run tests in a Single Directory

To run all the tests from one directory, use the directory as a parameter to pytest:

pytest tests/my-directory

Run tests in a Single Test File/Module

To run a file full of tests, list the file with the relative path as a parameter to pytest:

pytest tests/my-directory/test_demo.py

Run a Single Test Function

To run a single test function, add :: and the test function name:

pytest -v tests/my-directory/test_demo.py::test_specific_function

-v is used so you can see which function was run.

Run a Single Test Class

To run just a class, do like we did with functions and add ::, then the class name to the file parameter:

pytest -v tests/my-directory/test_demo.py::TestClassName

Run a Single Test Method of a Test Class

If you don’t want to run all of a test class, just one method, just add another :: and the method name:

pytest -v tests/my-directory/test_demo.py::TestClassName::test_specific_method

Run a Set of Tests Based on Test Name

The -k option enables you to pass in an expression to run tests that have certain names specified by the expression as a substring of the test name. It is possible to use and, or, and not to create complex expressions.

For example, to run all of the functions that have _raises in their name:

pytest -v -k _raises

回答 3

如果在两个不同的类中具有相同的方法名称,并且只想运行其中一个,则可以这样做:

pytest tests.py -k 'TestClassName and test_method_name'

If you have the same method name in two different classes and you just want to run one of them, this works:

pytest tests.py -k 'TestClassName and test_method_name'

回答 4

方法1:随机选择测试。长而丑。

python -m pytest test/stress/test_performance.py::TestPerformance::test_continuous_trigger test/integration/test_config.py::TestConfig::test_valid_config

方法2:使用关键字表达式。

注意:我正在按测试用例名称进行搜索。同样适用于TestClass名称。

情况1:无论找到什么,下面的内容都会运行。由于我们使用了’OR’。

python -m pytest -k 'test_password_valid or test_no_configuration'

可以说上述两个实际上是正确的,将运行2个测试。

情况2:现在一个不正确的名称和另一个正确的名称。

python -m pytest -k 'test_password_validzzzzzz or test_no_configuration' 

仅找到一个并运行。

情况3:如果要运行所有测试或不运行任何测试,请使用AND

python -m pytest -k 'test_password_valid and test_no_configuration'

如果正确或不执行,两者都将运行。

情况4:仅在一个文件夹中运行测试:

python -m pytest test/project1/integration -k 'test_password_valid or test_no_configuration'

情况5:仅从一个文件运行测试。

python -m pytest test/integration/test_authentication.py -k 'test_password_expiry or test_incorrect_password'

情况6:运行除匹配项外的所有测试。

python -m pytest test/integration/test_authentication.py -k 'not  test_incorrect_password'

Method 1: Randomly selected tests. Long and ugly.

python -m pytest test/stress/test_performance.py::TestPerformance::test_continuous_trigger test/integration/test_config.py::TestConfig::test_valid_config

Method 2: Use Keyword Expressions.

Note: I am searching by testcase names. Same is applicable to TestClass names.

Case 1: The below will run whichever is found. Since we have used ‘OR’ .

python -m pytest -k 'test_password_valid or test_no_configuration'

Lets say the two above are actually correct, 2 tests will be run.

Case 2: Now an incorrect name and another correct name.

python -m pytest -k 'test_password_validzzzzzz or test_no_configuration' 

Only one is found and run.

Case 3: If you want all tests to run or no one, then use AND

python -m pytest -k 'test_password_valid and test_no_configuration'

Both will be run if correct or none.

Case 4: Run test only in one folder:

python -m pytest test/project1/integration -k 'test_password_valid or test_no_configuration'

Case 5: Run test from only one file.

python -m pytest test/integration/test_authentication.py -k 'test_password_expiry or test_incorrect_password'

Case 6: Run all tests except the match.

python -m pytest test/integration/test_authentication.py -k 'not  test_incorrect_password'

回答 5

也许使用pytest_collect_file()钩子,您可以解析.txto.yaml文件的内容,在其中您可以根据需要指定测试,然后将它们返回到pytest核心。

pytest文档中显示了一个很好的示例。我认为您在寻找什么。

Maybe using pytest_collect_file() hook you can parse the content of a .txt o .yaml file where the tests are specify as you want, and return them to the pytest core.

A nice example is shown in the pytest documentation. I think what you are looking for.


回答 6

这是一个可能的部分答案,因为它仅允许选择测试脚本,而不能选择这些脚本中的单个测试。

而且它还受到我使用旧版兼容模式与unittest脚本的限制,因此不能保证它可以与本机pytest一起使用。

开始:

  1. 比如说创建一个新的字典subset_tests_directory
  2. ln -s tests_directory/foo.py
  3. ln -s tests_directory/bar.py

  4. 注意隐式假定文件位于的导入test_directory。我必须通过运行python foo.py,从内部进行subset_tests_directory并根据需要进行更正来修复其中的一些问题。

  5. 一旦测试脚本执行正确,公正cd subset_tests_directorypytest有。Pytest只会选择它看到的脚本。

另一种可能性是你当前的测试目录中的符号链接,说的ln -s foo.py subset_foo.py那么pytest subset*.py。这样可以避免调整导入,但在删除符号链接之前,事情会变得很混乱。也为我工作。

Here’s a possible partial answer, because it only allows selecting the test scripts, not individual tests within those scripts.

And it also limited by my using legacy compatibility mode vs unittest scripts, so not guaranteeing it would work with native pytest.

Here goes:

  1. create a new dictory, say subset_tests_directory.
  2. ln -s tests_directory/foo.py
  3. ln -s tests_directory/bar.py

  4. be careful about imports which implicitly assume files are in test_directory. I had to fix several of those by running python foo.py, from within subset_tests_directory and correcting as needed.

  5. Once the test scripts execute correctly, just cd subset_tests_directory and pytest there. Pytest will only pick up the scripts it sees.

Another possibility is symlinking within your current test directory, say as ln -s foo.py subset_foo.py then pytest subset*.py. That would avoid needing to adjust your imports, but it would clutter things up until you removed the symlinks. Worked for me as well.


回答 7

根据有关按节点ID运行测试的文档

由于您在foo.txt中拥有所有节点ID,因此只需运行

pytest `cat foo.txt | tr '\n' ' '`

这与以下命令相同(问题中包含文件内容)

pytest tests_directory/foo.py::test_001 tests_directory/bar.py::test_some_other_test

According to the doc about Run tests by node ids

since you have all node ids in foo.txt, just run

pytest `cat foo.txt | tr '\n' ' '`

this is same with below command (with file content in the question)

pytest tests_directory/foo.py::test_001 tests_directory/bar.py::test_some_other_test

在pytest中,conftest.py文件有什么用?

问题:在pytest中,conftest.py文件有什么用?

我最近发现了pytest。好像很棒 但是,我认为文档可能会更好。

我正在尝试了解conftest.py文件的用途。

在我的(目前很小的)测试套件conftest.py中,项目根目录下有一个文件。我用它来定义要注入测试的灯具。

我有两个问题:

  1. 这是正确的用法conftest.py吗?它还有其他用途吗?
  2. 我可以有多个conftest.py文件吗?我什么时候要这么做?示例将被理解。

更一般而言,您如何定义conftest.pypy.test测试套件中的目的和正确使用文件?

I recently discovered pytest. It seems great. However, I feel the documentation could be better.

I’m trying to understand what conftest.py files are meant to be used for.

In my (currently small) test suite I have one conftest.py file at the project root. I use it to define the fixtures that I inject into my tests.

I have two questions:

  1. Is this the correct use of conftest.py? Does it have other uses?
  2. Can I have more than one conftest.py file? When would I want to do that? Examples will be appreciated.

More generally, how would you define the purpose and correct use of conftest.py file(s) in a py.test test suite?


回答 0

这是conftest.py的正确用法吗?

是的。治具是潜在的和普遍的使用conftest.py。您将定义的固定装置将在测试套件中的所有测试之间共享。但是,在根目录中定义固定装置conftest.py可能没有用,如果所有测试未使用此类固定装置,则会减慢测试速度。

它还有其他用途吗?

是的,它确实。

  • 夹具:为测试使用的静态数据定义夹具。除非另有说明,否则套件中的所有测试都可以访问此数据。这可能是数据以及将传递给所有测试的模块帮助程序。

  • 外部插件加载conftest.py用于导入外部插件或模块。通过定义以下全局变量,pytest将加载模块并使它可用于其测试。插件通常是在项目或测试中可能需要的其他模块中定义的文件。您还可以按照此处的说明加载一组预定义的插件。

    pytest_plugins = "someapp.someplugin"

  • 挂钩:您可以指定挂钩(例如设置和拆卸方法)以及更多内容来改善测试。有关一组可用的挂钩,请阅读此处。例:

    def pytest_runtest_setup(item):
         """ called before ``pytest_runtest_call(item). """
         #do some stuff`
  • 测试根路径:这是一个隐藏功能。通过conftest.py在根路径中进行定义,pytest无需指定即可识别应用程序模块PYTHONPATH。py.test在后台sys.path通过包含从根路径找到的所有子模块来修改您的文件。

我可以有多个conftest.py文件吗?

是的,您可以,如果测试结构有些复杂,强烈建议您这样做。conftest.py文件具有目录范围。因此,创建有针对性的装置和助手是一个好习惯。

我什么时候要这么做?示例将不胜感激。

几种情况可能适合:

为一组特定的测试创建一组工具或挂钩

root / mod / conftest.py

def pytest_runtest_setup(item):
    print("I am mod")
    #do some stuff


test root/mod2/test.py will NOT produce "I am mod"

加载一组夹具用于某些测试,但不用于其他测试。

root / mod / conftest.py

@pytest.fixture()
def fixture():
    return "some stuff"

root / mod2 / conftest.py

@pytest.fixture()
def fixture():
    return "some other stuff"

root / mod2 / test.py

def test(fixture):
    print(fixture)

将打印“其他一些东西”。

覆盖从根继承的钩子conftest.py

root / mod / conftest.py

def pytest_runtest_setup(item):
    print("I am mod")
    #do some stuff

root / conftest.py

def pytest_runtest_setup(item):
    print("I am root")
    #do some stuff

通过在内部运行任何测试root/mod,仅打印“ I am mod”。

您可以conftest.py 在此处了解更多信息。

编辑:

如果我需要从不同模块中的多个测试中调用普通的辅助函数,该怎么办-如果将它们放在conftest.py中,它们将对我可用吗?还是我应该将它们放在helpers.py模块中,然后在测试模块中导入并使用它?

您可以conftest.py用来定义您的助手。但是,您应该遵循常规做法。辅助工具至少可以在中用作固定装置pytest。例如,在我的测试中,我有一个模拟的redis帮助器,可以通过这种方式将其注入到我的测试中。

根目录/helper/redis/redis.py

@pytest.fixture
def mock_redis():
    return MockRedis()

根/测试/材料/ conftest.py

pytest_plugin="helper.redis.redis"

根/测试/材料/ test.py

def test(mock_redis):
    print(mock_redis.get('stuff'))

这将是一个测试模块,您可以自由地将其导入测试中。注意,您可能会命名redis.pyconftest.py好像您的模块redis包含更多测试一样。但是,由于模棱两可,不鼓励这种做法。

如果要使用conftest.py,只需将该帮助程序放在根目录中conftest.py,并在需要时将其注入。

root / tests / conftest.py

@pytest.fixture
def mock_redis():
    return MockRedis()

根/测试/材料/ test.py

def test(mock_redis):
    print(mock_redis.get(stuff))

您可以做的另一件事是编写一个可安装的插件。在那种情况下,您的助手可以写在任何地方,但是需要定义一个要安装在您的和其他潜在测试框架中的入口点。看到这个

如果您不想使用固定装置,则当然可以定义一个简单的帮助器,并在需要的地方使用普通的旧导入。

root /测试/helper/redis.py

class MockRedis():
    # stuff

根/测试/材料/ test.py

from helper.redis import MockRedis

def test():
    print(MockRedis().get(stuff))

但是,由于模块不在测试的子文件夹中,因此此处可能存在路径问题。您应该能够克服这(未测试)通过添加__init__.py到您的帮助

root / tests / helper / __ init__.py

from .redis import MockRedis

或者只是将helper模块添加到您的中PYTHONPATH

Is this the correct use of conftest.py?

Yes it is. Fixtures are a potential and common use of conftest.py. The fixtures that you will define will be shared among all tests in your test suite. However, defining fixtures in the root conftest.py might be useless and it would slow down testing if such fixtures are not used by all tests.

Does it have other uses?

Yes it does.

  • Fixtures: Define fixtures for static data used by tests. This data can be accessed by all tests in the suite unless specified otherwise. This could be data as well as helpers of modules which will be passed to all tests.

  • External plugin loading: conftest.py is used to import external plugins or modules. By defining the following global variable, pytest will load the module and make it available for its test. Plugins are generally files defined in your project or other modules which might be needed in your tests. You can also load a set of predefined plugins as explained here.

    pytest_plugins = "someapp.someplugin"

  • Hooks: You can specify hooks such as setup and teardown methods and much more to improve your tests. For a set of available hooks, read here. Example:

    def pytest_runtest_setup(item):
         """ called before ``pytest_runtest_call(item). """
         #do some stuff`
    
  • Test root path: This is a bit of a hidden feature. By defining conftest.py in your root path, you will have pytest recognizing your application modules without specifying PYTHONPATH. In the background, py.test modifies your sys.path by including all submodules which are found from the root path.

Can I have more than one conftest.py file?

Yes you can and it is strongly recommended if your test structure is somewhat complex. conftest.py files have directory scope. Therefore, creating targeted fixtures and helpers is good practice.

When would I want to do that? Examples will be appreciated.

Several cases could fit:

Creating a set of tools or hooks for a particular group of tests.

root/mod/conftest.py

def pytest_runtest_setup(item):
    print("I am mod")
    #do some stuff


test root/mod2/test.py will NOT produce "I am mod"

Loading a set of fixtures for some tests but not for others.

root/mod/conftest.py

@pytest.fixture()
def fixture():
    return "some stuff"

root/mod2/conftest.py

@pytest.fixture()
def fixture():
    return "some other stuff"

root/mod2/test.py

def test(fixture):
    print(fixture)

Will print “some other stuff”.

Overriding hooks inherited from the root conftest.py.

root/mod/conftest.py

def pytest_runtest_setup(item):
    print("I am mod")
    #do some stuff

root/conftest.py

def pytest_runtest_setup(item):
    print("I am root")
    #do some stuff

By running any test inside root/mod, only “I am mod” is printed.

You can read more about conftest.py here.

EDIT:

What if I need plain-old helper functions to be called from a number of tests in different modules – will they be available to me if I put them in a conftest.py? Or should I simply put them in a helpers.py module and import and use it in my test modules?

You can use conftest.py to define your helpers. However, you should follow common practice. Helpers can be used as fixtures at least in pytest. For example in my tests I have a mock redis helper which I inject into my tests this way.

root/helper/redis/redis.py

@pytest.fixture
def mock_redis():
    return MockRedis()

root/tests/stuff/conftest.py

pytest_plugin="helper.redis.redis"

root/tests/stuff/test.py

def test(mock_redis):
    print(mock_redis.get('stuff'))

This will be a test module that you can freely import in your tests. NOTE that you could potentially name redis.py as conftest.py if your module redis contains more tests. However, that practice is discouraged because of ambiguity.

If you want to use conftest.py, you can simply put that helper in your root conftest.py and inject it when needed.

root/tests/conftest.py

@pytest.fixture
def mock_redis():
    return MockRedis()

root/tests/stuff/test.py

def test(mock_redis):
    print(mock_redis.get(stuff))

Another thing you can do is to write an installable plugin. In that case your helper can be written anywhere but it needs to define an entry point to be installed in your and other potential test frameworks. See this.

If you don’t want to use fixtures, you could of course define a simple helper and just use the plain old import wherever it is needed.

root/tests/helper/redis.py

class MockRedis():
    # stuff

root/tests/stuff/test.py

from helper.redis import MockRedis

def test():
    print(MockRedis().get(stuff))

However, here you might have problems with the path since the module is not in a child folder of the test. You should be able to overcome this (not tested) by adding an __init__.py to your helper

root/tests/helper/__init__.py

from .redis import MockRedis

Or simply adding the helper module to your PYTHONPATH.


回答 1

广义上,conftest.py是一个本地的按目录的插件。在这里,您可以定义目录特定的挂钩和固定装置。在我的情况下,有一个根目录,其中包含项目特定的测试目录。某些常见的魔术位于“ root” conftest.py中。特定于项目-在自己的项目中。除非将夹具广泛使用(在这种情况下,我宁愿直接在测试文件中定义它们),否则在conftest.py中存储夹具时不会发现任何不良情况。

In a wide meaning conftest.py is a local per-directory plugin. Here you define directory-specific hooks and fixtures. In my case a have a root directory containing project specific tests directories. Some common magic is stationed in ‘root’ conftest.py. Project specific – in their own ones. Can’t see anything bad in storing fixtures in conftest.py unless they are not used widely (In that case I prefer to define them in test files directly)


回答 2

我使用该conftest.py文件来定义要注入测试中的灯具,这是否正确使用conftest.py

是的,通常使用固定装置来准备好用于多个测试的数据。

它还有其他用途吗?

是的,灯具是pytest在实际测试功能之前(有时是之后)运行的功能。灯具中的代码可以执行您想要的任何操作。例如,夹具可以用于获取要进行测试的数据集,或者夹具也可以用于在运行测试之前使系统进入已知状态。

我可以有多个conftest.py文件吗?我什么时候要这么做?

首先,可以将灯具放入单独的测试文件中。但是,要在多个测试文件之间共享灯具,您需要在conftest.py所有测试的中央位置使用文件。灯具可以通过任何测试共享。如果您希望夹具仅由该文件中的测试使用,则可以将它们放在单独的测试文件中。

其次,可以,您可以conftest.py在top tests目录的子目录中拥有其他文件。如果这样做,这些较低级别conftest.py文件中定义的固定装置将可用于该目录和子目录中的测试。

最后,将固定装置放在conftest.py文件中的测试根目录下将使它们在所有测试文件中均可用。

I use the conftest.py file to define the fixtures that I inject into my tests, is this the correct use of conftest.py?

Yes, a fixture is usually used to get data ready for multiple tests.

Does it have other uses?

Yes, a fixture is a function that is run by pytest before, and sometimes after, the actual test functions. The code in the fixture can do whatever you want it to. For instance, a fixture can be used to get a data set for the tests to work on, or a fixture can also be used to get a system into a known state before running a test.

Can I have more than one conftest.py file? When would I want to do that?

First, it is possible to put fixtures into individual test files. However, to share fixtures among multiple test files, you need to use a conftest.py file somewhere centrally located for all of the tests. Fixtures can be shared by any test. They can be put in individual test files if you want the fixture to only be used by tests in that file.

Second, yes, you can have other conftest.py files in subdirectories of the top tests directory. If you do, fixtures defined in these lower-level conftest.py files will be available to tests in that directory and subdirectories.

Finally, putting fixtures in the conftest.py file at the test root will make them available in all test files.


pytest的PATH问题“ ImportError:没有名为YadaYadaYada的模块”

问题:pytest的PATH问题“ ImportError:没有名为YadaYadaYada的模块”

我使用easy_install在Mac上安装pytest,并开始为文件结构像这样的项目编写测试:

repo/
repo/app.py
repo/settings.py
repo/models.py
repo/tests/
repo/tests/test_app.py

py.test在repo目录中运行时,所有行为均符合您的预期

但是,当我在linux或Windows上尝试相同的操作时(两者上都装有pytest 2.2.3),只要它从我的应用程序路径中首次导入某些内容,它就会发出吠声。举例来说from app import some_def_in_app

我是否需要编辑PATH才能在这些系统上运行py.test?有人经历过吗?

I used easy_install to install pytest on a mac and started writing tests for a project with a file structure likes so:

repo/
repo/app.py
repo/settings.py
repo/models.py
repo/tests/
repo/tests/test_app.py

run py.test while in the repo directory, everything behaves as you would expect

but when I try that same thing on either linux or windows (both have pytest 2.2.3 on them) it barks whenever it hits its first import of something from my application path. Say for instance from app import some_def_in_app

Do I need to be editing my PATH to run py.test on these systems? Has Anyone experienced this?


回答 0

是的,如果您cd转到tests目录,则源文件夹不在Python的路径中。

您有2个选择:

  1. 手动将路径添加到测试文件,如下所示:

    import sys, os
    myPath = os.path.dirname(os.path.abspath(__file__))
    sys.path.insert(0, myPath + '/../')
  2. 使用env var运行测试PYTHONPATH=../

Yes, the source folder is not in Python’s path if you cd to the tests directory.

You have 2 choices:

  1. Add the path manually to the test files, something like this:

    import sys, os
    myPath = os.path.dirname(os.path.abspath(__file__))
    sys.path.insert(0, myPath + '/../')
    
  2. Run the tests with the env var PYTHONPATH=../.


回答 1

我不确定为什么py.test不会在PYTHONPATH本身中添加当前目录,但这是一种解决方法(将从存储库的根目录执行):

python -m pytest tests/

之所以有效,是因为Python为您添加了当前目录到PYTHONPATH中。

I’m not sure why py.test does not add the current directory in the PYTHONPATH itself, but here’s a workaround (to be executed from the root of your repository):

python -m pytest tests/

It works because Python adds the current directory in the PYTHONPATH for you.


回答 2

conftest

侵入性最低的解决方案是conftest.pyrepo/目录中添加一个空文件:

$ touch repo/conftest.py

而已。无需编写自定义代码来处理,sys.path也无需记住拖动PYTHONPATH或放入__init__.py不属于它的目录中。

之后的项目目录:

repo
├── conftest.py
├── app.py
├── settings.py
├── models.py
└── tests
     └── test_app.py

说明

pytestconftest在测试集合中寻找模块以收集自定义的钩子和固定装置,然后为了从中导入自定义的对象,请将pytest的父目录添加conftest.py到中sys.path(在本例中为repo目录)。

其他项目结构

如果你有其他的项目结构,将conftest.py包中的根目录(包含软件包,但不是包本身的人,所以并没有包含__init__.py),例如:

repo
├── conftest.py
├── spam
   ├── __init__.py
   ├── bacon.py
   └── egg.py
├── eggs
   ├── __init__.py
   └── sausage.py
└── tests
     ├── test_bacon.py
     └── test_egg.py

src 布局

尽管此方法可用于src布局(放置conftest.pysrc目录中):

repo
├── src
   ├── conftest.py
   ├── spam
      ├── __init__.py
      ├── bacon.py
      └── egg.py
   └── eggs 
       ├── __init__.py
       └── sausage.py
└── tests
     ├── test_bacon.py
     └── test_egg.py

请注意,添加srcPYTHONPATH减轻src布局的含义和好处!您将最终从存储库而不是已安装的软件包中测试代码。如果需要执行此操作,则可能根本不需要src目录。

从这往哪儿走

当然,conftest模块不仅仅是一些文件,可以帮助发现源代码。这是pytest框架的所有特定于项目的增强和测试套件的自定义的地方。pytestconftest整个文档中分散的模块上有很多信息; 开始于conftest.py:本地按目录插件

同样,SO在conftest模块上也有一个很好的问题:在py.test中,conftest.py文件的用途是什么?

conftest solution

The least invasive solution is adding an empty file named conftest.py in the repo/ directory:

$ touch repo/conftest.py

That’s it. No need to write custom code for mangling the sys.path or remember to drag PYTHONPATH along, or placing __init__.py into dirs where it doesn’t belong.

The project directory afterwards:

repo
├── conftest.py
├── app.py
├── settings.py
├── models.py
└── tests
     └── test_app.py

Explanation

pytest looks for the conftest modules on test collection to gather custom hooks and fixtures, and in order to import the custom objects from them, pytest adds the parent directory of the conftest.py to the sys.path (in this case the repo directory).

Other project structures

If you have other project structure, place the conftest.py in the package root dir (the one that contains packages but is not a package itself, so does not contain an __init__.py), for example:

repo
├── conftest.py
├── spam
│   ├── __init__.py
│   ├── bacon.py
│   └── egg.py
├── eggs
│   ├── __init__.py
│   └── sausage.py
└── tests
     ├── test_bacon.py
     └── test_egg.py

src layout

Although this approach can be used with the src layout (place conftest.py in the src dir):

repo
├── src
│   ├── conftest.py
│   ├── spam
│   │   ├── __init__.py
│   │   ├── bacon.py
│   │   └── egg.py
│   └── eggs 
│       ├── __init__.py
│       └── sausage.py
└── tests
     ├── test_bacon.py
     └── test_egg.py

beware that adding src to PYTHONPATH mitigates the meaning and benefits of the src layout! You will end up with testing the code from repository and not the installed package. If you need to do it, maybe you don’t need the src dir at all.

Where to go from here

Of course, conftest modules are not just some files to help the source code discovery; it’s where all the project-specific enhancements of the pytest framework and the customization of your test suite happen. pytest has a lot of information on conftest modules scattered throughout their docs; start with conftest.py: local per-directory plugins

Also, SO has an excellent question on conftest modules: In py.test, what is the use of conftest.py files?


回答 3

我有同样的问题。我通过__init__.pytests目录中添加一个空文件来修复它。

I had the same problem. I fixed it by adding an empty __init__.py file to my tests directory.


回答 4

pytest使用以下模块作为模块运行: python -m pytest tests

Run pytest itself as a module with: python -m pytest tests


回答 5

您可以在项目根目录中使用PYTHONPATH运行

PYTHONPATH=. py.test

或使用pip install作为可编辑导入

pip install -e .   # install package using setup.py in editable mode

You can run with PYTHONPATH in project root

PYTHONPATH=. py.test

Or use pip install as editable import

pip install -e .   # install package using setup.py in editable mode

回答 6

我创建此文件是为了回答您的问题和我自己的困惑。希望对您有所帮助。注意py.test命令行和tox.ini中的PYTHONPATH。

https://github.com/jeffmacdonald/pytest_test

具体来说:您必须告诉py.test和tox在哪里可以找到要包含的模块。

使用py.test可以做到这一点:

PYTHONPATH=. py.test

并使用tox,将其添加到tox.ini中:

[testenv]
deps= -r{toxinidir}/requirements.txt
commands=py.test
setenv =
    PYTHONPATH = {toxinidir}

I created this as an answer to your question and my own confusion. I hope it helps. Pay attention to PYTHONPATH in both the py.test command line and in the tox.ini.

https://github.com/jeffmacdonald/pytest_test

Specifically: You have to tell py.test and tox where to find the modules you are including.

With py.test you can do this:

PYTHONPATH=. py.test

And with tox, add this to your tox.ini:

[testenv]
deps= -r{toxinidir}/requirements.txt
commands=py.test
setenv =
    PYTHONPATH = {toxinidir}

回答 7

我在Flask中遇到了同样的问题。

当我添加时:

__init__.py

到测试文件夹,问题消失了:)

应用程序可能无法将文件夹测试识别为模块

I had the same problem in Flask.

When I added:

__init__.py

to tests folder, problem disappeared :)

Probably application couldn’t recognize folder tests as module


回答 8

我通过删除__init__.py源文件的父文件夹中的顶层来修复它。

I fixed it by removing the top-level __init__.py in the parent folder of my sources.


回答 9

ConftestImportFailure: ImportError('No module named ...当我不小心将__init__.py文件添加到src目录中时,我开始出现奇怪的错误(这不应该是Python包,而只是所有来源的容器)。

I started getting weird ConftestImportFailure: ImportError('No module named ... errors when I had accidentally added __init__.py file to my src directory (which was not supposed to be a Python package, just a container of all source).


回答 10

由于出现了一些更简单的事情(您甚至可以说微不足道),我遇到了此错误。我没有安装pytest模块。因此apt install python-pytest,为我修复了一个简单的问题。

“ pytest”将在setup.py中列为测试依赖项。确保同时安装测试要求。

I was getting this error due to something even simpler (you could even say trivial). I hadn’t installed the pytest module. So a simple apt install python-pytest fixed it for me.

‘pytest’ would have been listed in setup.py as a test dependency. Make sure you install the test requirements as well.


回答 11

我有一个类似的问题。pytest无法识别我在工作环境中安装的模块。

我也通过安装pytest到同一环境中来解决它。

I had a similar issue. pytest did not recognize a module installed in the environment I was working in.

I resolved it by also installing pytest into the same environment.


回答 12

对我来说,这个问题是tests.py由Django和tests目录生成的。删除tests.py解决了问题。

For me the problem was tests.py generated by Django along with tests directory. Removing tests.py solved the problem.


回答 13

我错误地使用了相对导入,因此出现了此错误。在OP示例中,test_app.py应该使用例如

from repo.app import *

但是,__ init__.py文件通常分散在文件结构中,除非文件和测试文件位于同一目录中,否则这将无法正常工作,并会产生类似ImportError的错误。

from app import *

这是我与一个项目有关的示例:

这是我的项目结构:

microbit/
microbit/activity_indicator/activity_indicator.py
microbit/tests/test_activity_indicator.py

为了能够从test_activity_indicator.py访问activity_indicator.py,我需要:

  • 使用正确的相对导入启动test_activity_indicatory.py:
    from microbit.activity_indicator.activity_indicator import *
  • 在整个项目结构中放置__init__.py文件:
    microbit/
    microbit/__init__.py
    microbit/activity_indicator/__init__.py
    microbit/activity_indicator/activity_indicator.py
    microbit/tests/__init__.py
    microbit/tests/test_activity_indicator.py

I got this error as I used relative imports incorrectly. In the OP example, test_app.py should import functions using e.g.

from repo.app import *

However liberally __init__.py files are scattered around the file structure, this does not work and creates the kind of ImportError seen unless the files and test files are in the same directory.

from app import *

Here’s an example of what I had to do with one of my projects:

Here’s my project structure:

microbit/
microbit/activity_indicator/activity_indicator.py
microbit/tests/test_activity_indicator.py

To be able to access activity_indicator.py from test_activity_indicator.py I needed to:

  • start test_activity_indicatory.py with the correct relative import:
    from microbit.activity_indicator.activity_indicator import *
  • put __init__.py files throughout the project structure:
    microbit/
    microbit/__init__.py
    microbit/activity_indicator/__init__.py
    microbit/activity_indicator/activity_indicator.py
    microbit/tests/__init__.py
    microbit/tests/test_activity_indicator.py

回答 14

通常,由于无法导入模块而导致测试中断。经过研究,我发现系统在错误的位置查看文件,我们可以通过在其中复制包含模块的文件来轻松解决问题。与所述文件夹相同,以便正确导入。另一个解决方案建议是更改导入的声明,并向MutPy显示单元的正确路径。但是,由于多个单元可以具有此依赖性,这意味着我们还需要在其声明中提交更改,因此,我们宁愿将单元简单地移动到文件夹中。

Very often the tests were interrupted due to module being unable to be imported,After research, I found out that the system is looking at the file in the wrong place and we can easily overcome the problem by copying the file, containing the module, in the same folder as stated, in order to be properly imported. Another solution proposal would be to change the declaration for the import and show MutPy the correct path of the unit. However, due to the fact that multiple units can have this dependency, meaning we need to commit changes also in their declarations, we prefer to simply move the unit to the folder.


回答 15

根据Dirk Avery在Medium上发表的一篇文章(并得到我的个人经验的支持),如果您在项目中使用虚拟环境,则无法在系统范围内安装pytest;您必须将其安装在虚拟环境中并使用该安装。

特别是,如果在两个地方都安装了该pytest命令,则仅运行该命令将不起作用,因为它将使用系统安装程序。正如其他答案所描述的,一个简单的解决方案是运行python -m pytest而不是pytest; 之所以有效,是因为它使用了环境版本的pytest。或者,您可以只卸载系统的pytest版本。重新激活虚拟环境后,该pytest命令应起作用。

According to a post on Medium by Dirk Avery (and supported by my personal experience) if you’re using a virtual environment for your project then you can’t use a system-wide install of pytest; you have to install it in the virtual environment and use that install.

In particular, if you have it installed in both places then simply running the pytest command won’t work because it will be using the system install. As the other answers have described, one simple solution is to run python -m pytest instead of pytest; this works because it uses the environment’s version of pytest. Alternatively, you can just uninstall the system’s version of pytest; after reactivating the virtual environment the pytest command should work.


回答 16

在遵循Flask教程时,我遇到了同样的问题,我在Pytest官方文档中找到了答案。 这与我(以及我认为还有许多其他人)用于做事的方式有些不同。

您必须setup.py至少使用以下两行在项目的根目录中创建一个文件:

from setuptools import setup, find_packages
setup(name="PACKAGENAME", packages=find_packages())

其中PACKAGENAME是您应用的名称。然后,您必须使用pip安装它:

pip install -e .

-e标志告诉pip以可编辑或“开发”模式安装软件包。因此,下次运行pytest该程序时,它将在标准版本中找到您的应用程序PYTHONPATH

I was having the same problem when following the Flask tutorial and I found the answer on the official Pytest docs It’s a little shift from the way I (and I think many others) are used to do things.

You have to create a setup.py file in your project’s root directory with at least the following two lines:

from setuptools import setup, find_packages
setup(name="PACKAGENAME", packages=find_packages())

where PACKAGENAME is your app’s name. Then you have to install it with pip:

pip install -e .

The -e flag tells pip to intall the package in editable or “develop” mode. So the next time you run pytest it should find your app in the standard PYTHONPATH.


回答 17

我的解决方案:

conftest.pytest包含以下内容的目录中创建文件:

import os
import sys
sys.path.insert(0,os.path.dirname(os.path.realpath(__file__)) + "/relative/path/to/code/")

这会将感兴趣的文件夹添加到python路径,而无需修改每个测试文件,设置env变量或弄乱绝对/相对路径。

My solution:

create the conftest.py file in the test directory containing:

import os
import sys
sys.path.insert(0,os.path.dirname(os.path.realpath(__file__)) + "/relative/path/to/code/")

This will add the folder of interest to the python path without modifying every test file, setting env variable or messing with absolute/relative paths.