如何让 pytest 不捕获异常

Posted

技术标签:

【中文标题】如何让 pytest 不捕获异常【英文标题】:How can I get pytest to not catch exceptions 【发布时间】:2020-10-06 17:46:38 【问题描述】:

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

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

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

这是一段在调试 pytest 测试时触发 AssertionError 时 vscode 没有中断的视频:

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

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

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

【问题讨论】:

它对我来说很好用,因为你给出了任何代码 sn-p 我只能猜测。如果您在测试中使用了with pytest.raises,那么您实际上已经捕获了异常,因此调试器不会中断 它对你来说很好,因为 pytest 本身退出时出现异常?我怀疑除非你以某种方式安排它,因为通常 pytest 会捕获异常,以便它可以在运行结束时报告哪些测试失败。您不同意 pytest 通常会捕获所有异常以便报告测试失败吗? 如果我没有with pytest.raises,它确实会崩溃 我们没有使用pytest.raises,它不会中断。我正在使用TEST:PYTHON 树视图中测试右侧的Debug 按钮运行。它会在Python Test Log 输出窗格中打印异常的名称,但不会中断。如果我在调试器中检查 Raised Exceptions 它将中断,但我不能这样做,因为有太多误报,我们引发和捕获的不相关异常。如果 vscode 允许您选择要中断的异常,那将有很大帮助。 我在原始问题中添加了一个视频,展示了 vscode 如何不会对我造成破坏。 【参考方案1】:

我用 pytest 提出了an issue,他们提出了一个可行的建议。将此添加到您的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,并且您只选中了未捕获的异常框,那么您将在未捕获异常之前出现的地方中断。

我还打开了an issue 和debugpy,他们有一些想法,但今天没有解决方案。 _PYTEST_RAISE 技巧 100% 为我解决了这个问题。

【讨论】:

【参考方案2】:

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

将文件 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 行。

你可以把这个钩子函数做成你喜欢的样子。

【讨论】:

谢谢,我可以试试这个。但是我很难理解 pytest 如何没有标志或模式。希望调试器在您的测试失败的地方中断似乎是世界上最自然的事情。人们一般如何调试测试? 我试过这个。它确实在那里中断,但没有堆栈,因此不能用于调试。我把图片和cmets放在原来的问题里。 我认为他们正在努力解决这个问题:github.com/microsoft/debugpy/issues/275 @Philip : 然后在调试控制台显示的行添加一个断点并重试 是的,可以运行 pytest,在控制台中查看异常发生的位置,在代码中找到该位置,然后在它引发之前在那里放置一个断点。我不认为这是这个问题的答案或非常好的解决方案。但是是的,它在紧要关头起作用,我一直在这样做。【参考方案3】:

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

解决方法:

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


def test_example():
    assert False


test_example()

结果:

注意事项:

    行为的重要区别与在launch.json 配置中指定"request": "test" 与指定"request": "launch" 相关。 (两种类型之间的区别在于 justMyCode 选项是否从全局启动配置中应用,如 here 所述)。 如果您指定了 python "request": "test",那么 VSCode 似乎不允许您像我在上面所做的那样在启动模式下调试文件。 在我的场景中,我的测试失败了,因为我在第三方库中引发了异常(当然是我的错)。第三方代码中的断点显然只有在添加 "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,
        //         
    ]

【讨论】:

以上是关于如何让 pytest 不捕获异常的主要内容,如果未能解决你的问题,请参考以下文章

如何在Qt中捕获异常?

Scala:静默捕获所有异常

前端捕获异常技巧总结

位置访问时无法捕获的异常

在 JSON.parse 期间在节点中捕获异常

求人指点下如何捕获SQL连接异常