Как обнаружить, когда тест теста pytest не удался?

Я использую pytest с селеном для автоматизации веб-сайта. Я хочу сделать снимок экрана только в случае сбоя теста. Я уже использовал 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() выполняется только тогда, когда нет ошибок

Ответ 1

Согласно вам post в stackoverflow, я могу поделиться тем, что я что-то на уме, надеюсь, это поможет: wink: То, что вы пытаетесь сделать, это обработать стандартное исключение AssertionError, которое может быть поднято assert или любым метод утверждения, реализованный в unittest.TestCase или, возможно, какой-либо пользовательский метод утверждения, который вызывает настраиваемое исключение. Есть 3 способа сделать это:

  • Используйте try-except-finally. Пример:

    try:
        Assert.fail("failed test")
    except AssertionError:
        get_screenshot()
        raise
    
  • Или используйте с, как менеджер контекста:

    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")
    

    здесь вы можете погрузиться глубже в том, как играть с ним

  • Последний, на мой взгляд, самый элегантный подход. Используя декораторы . С помощью этого декоратора вы можете украсить любой метод тестирования:

    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")
    

Ответ 2

После некоторой борьбы, в конечном итоге это сработало для меня.

В 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

Обратите внимание, что "запрос" - это "funcarg", который предоставляет pytest в контексте ваших тестов. Вам не обязательно определять его самостоятельно.

Источники: примеры pytest и thread on (not) making это проще.

Ответ 3

Вот как мы это делаем, отметим, что __ multicall __ имеет очень мало документации, и я помню, что чтение __ multicall __ будет устаревать, пожалуйста, используйте это с щепоткой соли и эксперимент с заменой __ multicall __ на элемент < , вызов "в соответствии с примерами.

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

Ответ 4

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))