如何让 pytest 不捕获异常

问题描述 投票:0回答:4

当我在 vscode 调试器中运行 pytest 并选中“未捕获的异常”时,并且存在测试错误,不会发生未捕获的异常,因为 pytest 会捕获它们并进行结果报告。我怎样才能告诉 pytest 让异常发生?这样我就可以在 vscode 调试器中捕获它们?

基本上我想要像

--pdb
这样的行为,但我希望它启动 vscode 调试器而不是 pdb。旗帜
--pdbcls
听起来很有希望,但不确定要给它什么
<module>:<class>

注意:通常我只会让它在引发异常时中断。但是我们的代码有大量引发但捕获的异常,因此这个选项没有用。

这是一个在调试 pytest 测试时触发 AssertionError 时 vscode 不会中断的视频:

@rioV8 下面的建议确实打破了异常,但由于某种原因,没有堆栈,这意味着你无法从那里调试:

我一定错过了一些东西,因为似乎没有人需要这种能力。但对我来说,这绝对是使用测试框架和调试器可以做的最基本、最简单的事情:作为一名开发人员,我想从引发错误的地方进行调试。

人们使用 pytest 调试器肯定有一些完全不同的方式,这是我忽略的一些明显的技术。

visual-studio-code vscode-debugger vscode-python
4个回答
29
投票

我向 pytest 提出了一个问题,他们提出了一个有效的建议。将其添加到您的

conftest.py

import os
import pytest

if os.getenv('_PYTEST_RAISE', "0") != "0":

    @pytest.hookimpl(tryfirst=True)
    def pytest_exception_interact(call):
        raise call.excinfo.value

    @pytest.hookimpl(tryfirst=True)
    def pytest_internalerror(excinfo):
        raise excinfo.value

然后在您的

"request": "test"
launch.json 中您可以切换该环境变量:

    {
        "name": "Debug Tests",
        "type": "python",
        "request": "test",
        "console": "integratedTerminal",
        "justMyCode": true,
        "env": {
            "_PYTEST_RAISE": "1"
        },
    }

如果设置了

_PYTEST_RAISE
并且仅选中 Uncaught Exceptions 框,您将在之前未引发未捕获异常的位置处中断。

我还与debugpy打开了

问题
,他们有一些想法,但今天没有解决方案。
_PYTEST_RAISE
技巧100%有效地为我解决了这个问题。


2
投票

这与上面的答案类似,但我更喜欢在调试测试时让它自动参与。将以下内容放入conftest.py中:

# conftest.py
import sys
import pytest

def is_debugging():
    return 'debugpy' in sys.modules
    
    
# enable_stop_on_exceptions if the debugger is running during a test
if is_debugging():
  @pytest.hookimpl(tryfirst=True)
  def pytest_exception_interact(call):
    raise call.excinfo.value
    
  @pytest.hookimpl(tryfirst=True)
  def pytest_internalerror(excinfo):
    raise excinfo.value

发生的事情是它检查“debugpy”是否处于活动状态。如果是,我们假设我们正在调试测试并在未捕获的异常上停止。否则,它会表现正常。这意味着如果您从命令行运行 pytest,“debugpy”不会处于活动状态,并且它将正常运行。一个很好的副作用是,如果存在any异常,包括导入异常、语言异常等,这也会停止调试器。


1
投票

我注意到一些可能相关的事情。尽管我现在还没有准备好深入挖掘,但我会将它们发布在这里,以防它们对某人有用。

解决方法:

我使用的一个简单技巧是(暂时)直接调用相关测试用例并“运行和调试”文件,而不是使用“调试测试用例”按钮:


def test_example():
    assert False


test_example()

结果:

备注:

  1. 行为上的重要差异与在
    "request": "test"
    配置中指定
    "request": "launch"
    与指定
    launch.json
    相关。 (两种类型之间还存在一个区别,即是否从全局启动配置应用
    justMyCode
    选项,如此处所述)。
  2. 如果您指定了 python
    "request": "test"
    ,那么 VSCode 似乎不会让您像我上面那样在启动模式下调试文件。
  3. 在我的场景中,由于我在第三方库中引起异常(当然是我的错),我的测试失败了。显然,只有添加
    "justMyCode": false
    选项才会触发第三方代码中的断点。

launch.json:

{
    "version": "0.2.0",
    "configurations": [
        
        {
            "name": "Python: Current File",
            "type": "python",
            "request": "launch",
            "program": "${file}",
            "console": "integratedTerminal",
            "justMyCode": false,            
        },
        // {
        //     "name": "Debug Unit Test",
        //     "type": "python",
        //     "request": "test",
        //     "console": "integratedTerminal",
        //     "justMyCode": false,
        // }        
    ]
}

0
投票

行为并不相同,但测试运行程序在未捕获的异常处停止。

将文件

conftest.py
添加到包含测试的目录中。

# conftest.py
def pytest_exception_interact(node, call, report):
  print( call.excinfo.traceback[0] )
  pass

这为任何未捕获的异常添加了一个钩子。

在带有

pass
的行放置一个断点。

然后

Debug
测试。

查看调试控制台,您会看到发生异常的文件和行号。

test_example.py F  File 'path\\test_example.py':4 in test_example
  assert False

调试器停在

pass
行。

你可以随心所欲地制作这个钩子函数。

© www.soinside.com 2019 - 2024. All rights reserved.