在使用 py.test 时,我可以使用 python 调试器进行调试吗?

我使用 py.test 对我的 python 程序进行单元测试。我希望用 python 调试器以正常的方式调试我的测试代码(我指的是代码中的 pdb.set _ trace ()) ,但是我不能让它工作。

在代码中放入 pdb.set _ trace ()不起作用(引发 IOError: 在捕获输出时读取 stdin)。我还尝试使用这个选项运行 py.test —— pdb,但是如果我想了解断言之前发生了什么,那么这个选项似乎不起作用。当断言失败时,它就会中断,从该行继续下去意味着终止程序。

有人知道调试的方法吗,还是说调试和 py.test 不应该在一起?

84689 次浏览

I'm not familiar with py.test, but for unittest, you do the following. Maybe py.test is similar:

In your test module (mytestmodule.py):

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

Then run the test with

python -m pdb mytestmodule.py

You will get an interactive pdb shell.

Looking at the docs, it looks like py.test has a --pdb command line option:

https://docs.pytest.org/en/7.2.x/reference/reference.html#command-line-flags

it's real simple: put an assert 0 where you want to start debugging in your code and run your tests with:

py.test --pdb

done :)

Alternatively, if you are using pytest-2.0.1 or above, there also is the pytest.set_trace() helper which you can put anywhere in your test code. Here are the docs. It will take care to internally disable capturing before sending you to the pdb debugger command-line.

I found that I can run py.test with capture disabled, then use pdb.set_trace() as usual.

> py.test --capture=no
============================= test session starts ==============================
platform linux2 -- Python 2.5.2 -- pytest-1.3.3
test path 1: project/lib/test/test_facet.py


project/lib/test/test_facet.py ...> /home/jaraco/projects/project/lib/functions.py(158)do_something()
-> code_about_to_run('')
(Pdb)

The easiest way is using the py.test mechanism to create breakpoint

http://pytest.org/latest/usage.html#setting-a-breakpoint-aka-set-trace

import pytest
def test_function():
...
pytest.set_trace()    # invoke PDB debugger and tracing

Or if you want pytest's debugger as a one-liner, change your import pdb; pdb.set_trace() into import pytest; pytest.set_trace()

Similar to Peter Lyon's answer, but with the exact code you need for pytest, you can add the following to the bottom of your pytest module (my_test_module.py) :

if __name__ == "__main__":
pytest.main(["my_test_module.py", "-s"])

Then you can invoke the debugger from the command line:

pdb3 my_test_module.py

Boom. You're in the debugger and able to enter debugger commands. This method leaves your test code un-littered with set_trace() calls and will run inside pytest 'normally'.

Add and remove breakpoints without editing source files

Although you can add breakpoints by adding breakpoint() or set_trace() statements to your code, there are two issues with this approach:

  • Firstly, once you have started running your code, there is no way to remove your breakpoint. I often find that once I start running my code and reach an initial breakpoint, I want to place another one and remove the initial breakpoint. After breakpoint() drops me into the debugger I can add additional breakpoints, but I can't remove the initial one. Although this can be mitigated somewhat by putting the initial breakpoint statement higher up, if you have parametrised tests then even that is limited. I may find myself repeating cont very often.
  • Secondly, it requires changes to the source code. You need to remember to remove all breakpoint() commands before committing any code to version control, you have to remove them before switching branches, etc. I sometimes find I want to use the debugger to compare test runs between two branches, and having to edit the source code to add a breakpoint every time makes that a considerably slower and more error-prone exercise. I may even want to add a breakpoint in a library I'm calling, in which case the file I'm editing may not even me in my git repository but somewhere deep in my conda environment, increasing the risk of forgetting to remove it. Editing files to add break points is, in my humble opinion, ugly.

To add and remove breakpoints interactively without editing any source files, you can evoke pytest as follows (in the bash shell):

python -mipdb $(type -p pytest) -s test_fileset.py

The -s flag is crucial here, because it stops pytest from messing with stdin and stdout, and when running inside the debugger, pytest will fail to mess with stdin and stdout and everything will go wrong. The exact calling syntax will be different for different shells.

Simply use: pytest --trace test_your_test.py. This will invoke the Python debugger at the start of the test