如何检测pytest测试用例何时失败?

How to detect when pytest test case failed?

我正在使用 pytest 和 selenium 来自动化网站。我只想在测试用例失败时拍摄一些屏幕截图。我以前使用过 TestNG,对于 TestNG,使用 ITestListner 非常简单。 我们在 pytest 中有类似的东西吗?

我尝试使用 teardown_method() 来实现这一点 但是当测试用例失败时,这个方法不会被执行。

import sys

from unittestzero import Assert
class TestPY:
    def setup_method(self, method):
        print("in setup method")
        print("executing " + method.__name__)

    def teardown_method(self, method):
        print(".....teardown")
        if sys.exc_info()[0]:
            test_method_name = method
            print test_method_name

    def test_failtest(self):
        Assert.fail("failed test")

teardown_method() 只有在没有失败时才执行

这就是我们的做法,注意 __multicall__ 的文档非常少,我记得读过 __multicall__ 将被弃用,请用少许盐使用它并尝试将 __multicall__ 替换为 'item, call'根据示例。

def pytest_runtest_makereport(__multicall__):
    report = __multicall__.execute()

    if report.when == 'call':
        xfail = hasattr(report, 'wasxfail')
        if (report.skipped and xfail) or (report.failed and not xfail):

           try:
              screenshot = APP_DRIVER.take_screen_shot(format="base64")


           except Exception as e:
              LOG.debug("Error saving screenshot !!")
              LOG.debug(e)

    return report

根据您在 Whosebug 上 的说法,我可以分享我的想法,我希望它会 help:wink: 您要做的是处理标准 AssertionError 异常,该异常可以由 assert keyword or by any assertion method implemented in unittest.TestCase 或任何引发自定义异常的自定义断言方法引发。 有 3 种方法可以做到这一点:

  1. 使用try-except-finally构造。一些基本示例:

    try:
        Assert.fail("failed test")
    except AssertionError:
        get_screenshot()
        raise
    
  2. 或使用with语句,作为上下文管理器:

    class TestHandler:
        def __enter__(self):
            #  maybe some set up is expected before assertion method call
            pass
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            #  catch whether exception was raised
            if isinstance(exc_val, AssertionError):
                get_screenshot()
    
    
    with TestHandler():
        Assert.fail("failed test")
    

    here you can dive deeper on how to play with it

  3. 在我看来,最后一种是最优雅的方法。使用 decorators。使用这个装饰器你可以装饰任何测试方法:

    def decorator_screenshot(func):
        def wrapper(*args, **kwargs):
            try:
               func(*args, **kwargs)
            except AssertionError:
               get_screenshot()
               raise
        return wrapper
    
    
    @decorator_screenshot
    def test_something():
        Assert.fail("failed test")
    

经过一番努力,最终这对我有用。

在conftest.py中:

@pytest.hookimpl(hookwrapper=True, tryfirst=True)
def pytest_runtest_makereport(item, call):
    outcome = yield
    rep = outcome.get_result()
    setattr(item, "rep_" + rep.when, rep)
    return rep

并且,在您的代码中,在夹具中(例如,在用于测试的拆卸夹具中)像这样使用它:

def tear_down(request):
    method_name = request.node.name
    if request.node.rep_call.failed:
        print('test {} failed :('.format(method_name))
        # do more stuff like take a selenium screenshot

请注意,"request" 是 pytest 在您的测试上下文中提供的固定装置 "funcarg"。您不必自己定义它。

来源:pytest examples and thread on (not) making this easier.

def pytest_runtest_makereport(item, call):
    if call.when == 'call':
        if call.excinfo is not None:
            # if excinfor is not None, indicate that this test item is failed test case
            error("Test Case: {}.{} Failed.".format(item.location[0], item.location[2]))
            error("Error: \n{}".format(call.excinfo))