[Code Recipe] Получение дебаг-информации об упавшем webdriver тесте в py.test

Фабула. До какого-то времени все тестовые классы наследовались от родного пайтоновского unittest.TestCase’а, а пайтест использовался для параллелизации и автоматического построения отчётов о тестировании. Со временем, мы решили отказаться от использования unittest’а в пользу пайтеста ради хороших фичей (удобная параметризация тестов, например), но тут же столкнулись с проблемой получения некоей информации об упавшем тесте (урл, скриншот, юзер и т.д.). Раскопки гугла и толчок в верном направлении от @polusok натолкнули на следующее решение.

В нашей практике мы используем базовый тестовый класс, в котором объявлено несколько кастомных методов для общего использования. В нём создаём некий метод get_fail_debug(), который вернёт нам всю необходимую информацию.

def get_fail_debug(self):
        """Failed test report generator"""
        print 'Fail debug info:\n'
        alerts = 0
        try:
            while self.driver.switch_to_alert():
                alert = self.driver.switch_to_alert()
                print 'Unexpected ALERT: ' + alert.text
                alerts += 1
                alert.dismiss()
        except:
            if alerts != 0:
                print ''
            pass
        url = self.get_fail_url()   # Custom method to get url with autologin key for quick access
        print 'Fail URL: ' + url
        now = datetime.now().strftime('%d_%H-%M-%S-%f')  # For creating unique filenames
        if not os.path.exists("/opt/workspace/screenshots/"):
            os.makedirs("/opt/workspace/screenshots/")
        try:
            self.driver.save_screenshot('/opt/workspace/screenshots/%s.png' % now)
            fail_screenshot_url = 'http://some_service/screenshots/%s.png' % now                
        except Exception as e:
            fail_screenshot_url = 'No screenshot was captured due to exception %s' % e.message
        print 'Fail SCRENSHOT: ' + fail_screenshot_url
        if self.logged_in_user:
            user = 'id: %s, email: %s' % (self.logged_in_user.id, self.logged_in_user.email)
        else:
            user = 'Guest'
        print 'User: ' + user

в setup_method(self, method) того-же базового класса добавляем такую строку

pytest.config.get_fail_debug = self.get_fail_debug

На выходе имеем, что каждый отдельно взятый тест в курсе об этом методе
Ну и самое интересное, в conftest.py объявляем функцию, в которой куча всякой магии, но нужно поверить, что так делать правильно:

def pytest_runtest_makereport(__multicall__, item, call):
    """pytest failed test report generator"""
    report = __multicall__.execute()
    if report.when == 'call':
        report.session_id = getattr(item, 'session_id', None)
        if report.skipped and 'xfail' in report.keywords or report.failed and 'xfail' not in report.keywords:
            try:
                item.config.get_fail_debug()  # Вызываем тот самый метод из базового класса
            except:
                print 'Error collecting debug information. Webdriver instance must have crushed'
    return report

Ну и и поломанный тест выглядит примерно так:

<Пропущенный трейсбэк>
E       assert None                                                                                                                                                                                                                          
----------------- Captured stdout -------------
Fail debug info:                                                                                                                                                                                                                             

Fail URL: http://example.com/some_page.html
Fail SCRENSHOT: http://some_service/screenshots/14_19-56-21-389139.png
User: Guest

Надеюсь, кому-то поможет.

PS На правах рекламы, @polusok обещает делиться секретами мастерства за вот такие вот заметки :wink:
PPS Сделайте выбор нескольких рубрик, что ли :smile:

5 лайков

Спасибо @furiousduck за code recipe, рад что у тебя все получилось и ты теперь используешь прелести py.test. На счет рубрик, думаю будут лейблы, но за фидбек спасибо.

Кстати, @furiousduck было бы хорошо, чтобы ты код оформил в нашем репозитории примеров at.info-knowledge-base by atinfo