Прикрепление скриншотов через selene к allure-отчету

Коллеги, приветствую.
Использую в проекте связку pytest+selene+allure. Отчеты по тестам планирую размещать через Jenkins. Возник такой вопрос:
По умолчанию, selene сохраняет скриншот при фейле в системную папку пользователя, в отчете, соответственно, содержится только информация о пути к файлу. А хотелось бы, чтобы скриншоты были прикреплены к самому отчету.
Оборачивать каждую проверку в try-except + allure.attach - явно неэффективный путь.

Нет ли более красивых способов ?

P.S. Версию selene использую предрелизную (pip install selene --pre)

Посмотрите тут Как записать скриншот если тест падает allure ?

Спасибо, за наводку. Даж когда-то читал эту тему xD. В этом варианте мне придется каждый тест(или класс) отметить фикстурой. Неплохо. Но хочется лучше.
Selene так и так делает скриншот. Всего-то нужно изменить подход: вместо сохранения в папку, прикреплять к отчету. Может у разработчиков есть планы по реализации такого функционала через конфиг ? @Ayia @Sergey_Pirogov

Не вместо, а просто крепить к отчету. В selene есть метод get_latest_screenshot

Да, метод такой есть. И я так понимаю, возможный вариант его использования следующий:
в базовый класс тестов в teardown_method я прописываю функцию, которая будет проверять, есть ли последний скриншот, и если есть, то прикреплять его к отчету.

Безусловно, это вариант будет лучше, чем прописывать фикстуры для каждого класса. Но отчет будет выглядеть коряво: файл со скриншот прикрепится в самый конец (а в не шаге с ошибкой) + будет информация о скриншоте, сохраненным на диск (а этот файл скорее всего не будет доступен пользователю). Ну и сам скриншот будет занимать места на харде в два раза больше.
Все верно?

Не верно. Посмотрите на форуме уже рассказывали как на экстеншин поставить вызов скриншота и прикрепить к отчету

Добрый день, подскажите. Как вы реализовали добавления скриншота к отчету. Спасибо

Пока остановился на варианте с хуком pytest_exception_interact

Скриншот прикрепляется к отчету, но и дублируется в папку, указанную в конфиге selene.

Наверное глупый вопрос, но где мне найти этот файл conftest.py ? Использую virtualenv мне непосредственно там надо найти и прописать pytest_exception_interact в conftest.py?

Файл conftest.py создается вручную и размещается в папке с тестами. Например, в такой структуре:

project_folder
  - tests
      -- conftest.py
      -- test_something.py
      -- test_something_2.py

Структуру понял, спасибо.
Добавил файл в conftest.py.
Вот такого он формата:
#!/usr/bin/env python
# -- coding: utf-8 --
# coding: utf8

import allure

def pytest_exception_interact(node, call, report):
** driver = node.instance.driver**
** allure.attach(**
** name=‘Скриншот’,**
** contents=driver.get_screenshot_as_png(),**
** type=allure.constants.AttachmentType.PNG,**
** )**

Но при запуске такое выводит:

INTERNALERROR> Traceback (most recent call last):
INTERNALERROR> File “/Users/user/Desktop/autotesting2/config/lib/python2.7/site-packages/_pytest/main.py”, line 110, in wrap_session
INTERNALERROR> session.exitstatus = doit(config, session) or 0
INTERNALERROR> File “/Users/user/Desktop/autotesting2/config/lib/python2.7/site-packages/_pytest/main.py”, line 146, in _main
INTERNALERROR> config.hook.pytest_runtestloop(session=session)
INTERNALERROR> File “/Users/user/Desktop/autotesting2/config/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py”, line 745, in call
INTERNALERROR> return self._hookexec(self, self._nonwrappers + self._wrappers, kwargs)
INTERNALERROR> File “/Users/user/Desktop/autotesting2/config/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py”, line 339, in _hookexec
INTERNALERROR> return self._inner_hookexec(hook, methods, kwargs)
INTERNALERROR> File “/Users/user/Desktop/autotesting2/config/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py”, line 334, in
INTERNALERROR> _MultiCall(methods, kwargs, hook.spec_opts).execute()
INTERNALERROR> File “/Users/user/Desktop/autotesting2/config/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py”, line 614, in execute
INTERNALERROR> res = hook_impl.function(*args)
INTERNALERROR> File “/Users/user/Desktop/autotesting2/config/lib/python2.7/site-packages/_pytest/main.py”, line 169, in pytest_runtestloop
INTERNALERROR> item.config.hook.pytest_runtest_protocol(item=item, nextitem=nextitem)
INTERNALERROR> File “/Users/user/Desktop/autotesting2/config/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py”, line 745, in call
INTERNALERROR> return self._hookexec(self, self._nonwrappers + self._wrappers, kwargs)
INTERNALERROR> File “/Users/user/Desktop/autotesting2/config/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py”, line 339, in _hookexec
INTERNALERROR> return self._inner_hookexec(hook, methods, kwargs)
INTERNALERROR> File “/Users/user/Desktop/autotesting2/config/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py”, line 334, in
INTERNALERROR> _MultiCall(methods, kwargs, hook.spec_opts).execute()
INTERNALERROR> File “/Users/user/Desktop/autotesting2/config/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py”, line 613, in execute
INTERNALERROR> return _wrapped_call(hook_impl.function(*args), self.execute)
INTERNALERROR> File “/Users/user/Desktop/autotesting2/config/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py”, line 254, in _wrapped_call
INTERNALERROR> return call_outcome.get_result()
INTERNALERROR> File “/Users/user/Desktop/autotesting2/config/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py”, line 280, in get_result
INTERNALERROR> _reraise(*ex) # noqa
INTERNALERROR> File “/Users/user/Desktop/autotesting2/config/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py”, line 265, in init
INTERNALERROR> self.result = func()
INTERNALERROR> File “/Users/user/Desktop/autotesting2/config/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py”, line 613, in execute
INTERNALERROR> return _wrapped_call(hook_impl.function(*args), self.execute)
INTERNALERROR> File “/Users/user/Desktop/autotesting2/config/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py”, line 254, in _wrapped_call
INTERNALERROR> return call_outcome.get_result()
INTERNALERROR> File “/Users/user/Desktop/autotesting2/config/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py”, line 280, in get_result
INTERNALERROR> _reraise(*ex) # noqa
INTERNALERROR> File “/Users/user/Desktop/autotesting2/config/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py”, line 265, in init
INTERNALERROR> self.result = func()
INTERNALERROR> File “/Users/user/Desktop/autotesting2/config/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py”, line 613, in execute
INTERNALERROR> return _wrapped_call(hook_impl.function(*args), self.execute)
INTERNALERROR> File “/Users/user/Desktop/autotesting2/config/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py”, line 254, in _wrapped_call
INTERNALERROR> return call_outcome.get_result()
INTERNALERROR> File “/Users/user/Desktop/autotesting2/config/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py”, line 280, in get_result
INTERNALERROR> _reraise(*ex) # noqa
INTERNALERROR> File “/Users/user/Desktop/autotesting2/config/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py”, line 265, in init
INTERNALERROR> self.result = func()
INTERNALERROR> File “/Users/user/Desktop/autotesting2/config/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py”, line 614, in execute
INTERNALERROR> res = hook_impl.function(*args)
INTERNALERROR> File “/Users/user/Desktop/autotesting2/config/lib/python2.7/site-packages/_pytest/runner.py”, line 68, in pytest_runtest_protocol
INTERNALERROR> runtestprotocol(item, nextitem=nextitem)
INTERNALERROR> File “/Users/user/Desktop/autotesting2/config/lib/python2.7/site-packages/_pytest/runner.py”, line 82, in runtestprotocol
INTERNALERROR> reports.append(call_and_report(item, “call”, log))
INTERNALERROR> File “/Users/user/Desktop/autotesting2/config/lib/python2.7/site-packages/_pytest/runner.py”, line 168, in call_and_report
INTERNALERROR> hook.pytest_exception_interact(node=item, call=call, report=report)
INTERNALERROR> File “/Users/user/Desktop/autotesting2/config/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py”, line 745, in call
INTERNALERROR> return self._hookexec(self, self._nonwrappers + self._wrappers, kwargs)
INTERNALERROR> File “/Users/user/Desktop/autotesting2/config/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py”, line 339, in _hookexec
INTERNALERROR> return self._inner_hookexec(hook, methods, kwargs)
INTERNALERROR> File “/Users/user/Desktop/autotesting2/config/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py”, line 334, in
INTERNALERROR> _MultiCall(methods, kwargs, hook.spec_opts).execute()
INTERNALERROR> File “/Users/user/Desktop/autotesting2/config/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py”, line 614, in execute
INTERNALERROR> res = hook_impl.function(*args)
INTERNALERROR> File “/Users/user/Desktop/autotesting2/conftest.py”, line 9, in pytest_exception_interact
INTERNALERROR> driver = node.instance.driver
INTERNALERROR> AttributeError: TestSomeCase instance has no attribute ‘driver’

Или файл надо положить в virtualenv ?

Конечно, pytest traceback’i , бывают непонятными, но в данном случае, все очевидно.
В объекте класса TestSomeCase нет свойства ‘driver’

Каким образом у тебя драйвер попадает в тестовый класс ?

Использую Selene, при импорте его, как я понимаю и подтягивается драйвер.
Или його явно надо объявить ?

значит попробуй его импортнуть в этот хук напрямую:


def pytest_exception_interact(node, call, report):
    from selene.browser import driver    
    # ...
    allure.attach(
        name='Скриншот',
        contents=driver().get_screenshot_as_png(),
        type=allure.constants.AttachmentType.PNG,
    )
    # ...

Данный хук работает, вот как выглядит финальный файл для селена

def pytest_exception_interact(node, call, report):
driver = node.instance.driver
# …
allure.attach(
name=‘Скриншот’,
body=browser.driver().get_screenshot_as_png(),
attachment_type=allure.attachment_type.PNG,
)
# …

  • надо явно указать драйвер перед кейсами (для селена это так driver = browser.driver())

тесты запускал в PyCharm? из консоли ошибка та же? INTERNALERROR может быть от самого PyCharm

Перенес тесты на сервер. Теперь при “фэйле” тестов ошибка в файле conftest.py в котором лежит мой хук

INTERNALERROR> File “/home/autotesting2/conftest.py”, line 20, in pytest_exception_interact
INTERNALERROR> attachment_type=allure.attachment_type.PNG,
INTERNALERROR> AttributeError: ‘module’ object has no attribute ‘attachment_type’

Как я понимаю что какой-то модуль не установлен, может подскажите какой ?

Я полагаю, что дело не в модуле. Как-то криво код написан. Насколько я вижу по коду allure там нет свойства attachment_type.

Попробуй, как в моем примере:

Так и не смог найти информацию про то как работает driver = node.instance.driver.
Я так понимаю он пытается взять у класса driver. В самом классе у меня driver не прописывается, я его прописываю в функцию:

 def test_check_price(self, driver, number_product, additionally__to_website)

Сам драйвер прописывается в conftest:

class ChromeDriverManager(object):

    def __init__(self):
        self._instance = None

    def start(self, type='ff'):
        chrome_options = Options()
        chrome_options.add_argument("--headless")
        chrome_options.add_argument("--window-size=1920x1080")
        self._instance = webdriver.Chrome()
        # self._instance = webdriver.Chrome(chrome_options=chrome_options)
        return self._instance

    @property
    def instance(self):
        if not self._instance:
            self.start()
        return self._instance

    def stop(self):
        self._instance.quit()
        self._instance = None

Я не в полной мере понимаю как это все отрабатывает. Все работает - это главное.
Мне как то надо прописать в классе драйвер? Или в хук закинуть драйвер другим способом?

У меня ошибка аналогичная:

INTERNALERROR> AttributeError: TestSomeCase instance has no attribute ‘driver’

Я вставляю скрины, когда тест фейлится. Pytest + appium+allure.
Делаю это через хук pytest_runtest_makereport:

@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


@pytest.fixture(autouse=True, scope='session')
def driver(request, _device):
    import allure
    from appium import webdriver
    from config.devices import devices
    opt = _device
    driver = webdriver.Remote(
        command_executor='http://127.0.0.1:4723/wd/hub',
        desired_capabilities=devices[opt]
    )
    driver.set_page_load_timeout(7)

    yield driver

    if request.node.rep_call.failed:
        # Make the screen-shot if test failed:
        try:
            allure.attach(
                driver.get_screenshot_as_png(),
                name=request.function.__name__,
                attachment_type=allure.attachment_type.PNG
            )
        except:
            pass
    driver.close_app()
1 лайк