py.test. Переиспользование тестовых сценариев

Всем привет! Пишу UI тесты на Python с использованием фреймворка py.test + PageObject.
Все как обычно, есть отдельно описание страниц (Page Object) и есть для каждой страницы отдельный класс с тестами элементов страницы (сами проверки)

Возникла следующая идея:

Есть потребность составления тестов в формате пользовательских сценариев (зашел на сайт -> перешел в раздел -> заполнил форму и т.д)

Хотелось бы сделать файлы с проверками отдельных страниц (например: проверка страницы с некой формой, где проверяется наличие верных полей, кнопок и т.д)
и затем использовать эти же проверки в тесте с пользовательскими сценариями.

Пример: Захожу на сайт -> перешел в раздел -> вижу форму: запускаются проверки из файла, где проверяются все элементы формы -> затем еще какие-то действия.

Как думаете, есть ли возможность такое реализовать с помощью py.test или вообще с помощью каких-то тестовых фреймворков?

Привет, пока не совсем понятно, КАК эти ваши тесты отличаются от самых обычных тестов на Selenium. Т.к. прелесть UI тестов как раз в том, что они могут и должны проверять пользовательские сценарии. Больше пользовательских сценариев покрыто = больше толку от тестов.

Давайте представим такой сценарий:
Проверка написания отзыва в системе отзывов. Чтобы написать отзыв, нужно быть зерегистрированным.

Идеи по оформлению сценария:

  1. метим тест фикстурой, что она выполняется из под пользователя, а не гостя. В этом случае фикстура может как получать уже готовые данные логина (из ресурса с тестовыми данными), так и получать их в случайном порядке из БД (бд хелпер функция возвращающая удовлетворяющие условию данные). Фикстура эта будет производить логин непосредственно перед тестированием самого кейса. Либо в самом кейсе,
  2. Захожу на форму написания отзыва(точка входа? прямой линк или клиентский рендеринг приложения, т.е. клик в какую-нибудь кнопку? - оба сценария имеют право на жизнь), делаю все проверки поведения, присущие этой форме: проверяю негативные сценарии (наличие JS-popup’ов-заглушек), проверив все возможные кейсы - выполняю успешный сценарий публикации отзыва. Если отзыв при публикации не перенаправляет на общую ленту отзывов, можно перейти на страницу, где он будет виден в общем списке и найти его по тексту сообщения. Всё.

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

В большинстве случаев, на разные страницы пишутся разные тесты, и если в рамках тестирования страницы отзыва вам нужно делать манипуляции со страницей логина, то этот шаг нужно считать подготовительным и не обременять его проверками на негативное тестирование - этот шаг будет контекстом. Контекст теста никогда не тестируется и принимается, что он должен просто быть корректным.

При этом всём, никогда не зазорно при получении инстанса новой страницы брать и проверять существование элементов - это не обходится дополнительными расходами на время выполнения текущего сценария и позволяет понять по css состояние элементов, тексты и их номинальное соответствие спецификации.

В современных веб приложениях все компоненты имеют слабосвязанную архитектуру, и прежде чем делать кейсы в автоматизации UI, которые должны как-то стрессово протестировать сайт, нужно как минимум иметь какие-то основания на этот счет. Но в большинстве своём, повторюсь, изолированные тесты каждого компонента, который вписывается в пользовательский сценарий - с наличием акцентов для текущего сценария - это на мой взгляд лучшее решение. Таким образом при прогоне всех тестов на все пользовательские сценарии будет протестировано поведение всех страниц при миинмальном повторении кода и затратах времени на выполнение.

Пример самого простого пользовательского сценария представлен ниже.

def test_chat_as_bac(self):
    """
    Проверяем клиентский рендеринг и написание из под роли Компании
    """
    login_as(self.driver, users['pac'])
    start_page = FirstPage(self.driver)
    messages_page = start_page.enter_messages()  # переходим из хедера в личные сообщения
    message_text = get_random_99()  # генерируем случайный текст
    messages_page.chat_with(users['basic']).as_company().send_message(message_text)  # находим собеседника, выбираем роль от лица компании, вводим текст в поле ввода, отправляем сообщение
    assert messages_page.last_message_text(message_text)  # проверяем, что текст сообщения отобразился в диалоге последним
    assert messages_page.message_author_is_bac  # проверяем, что иконка отправителя соответствует выбранной роли, от лица кого пишем

В момент перехода на начальную страницу мы можем проверить все ее элементы - на наличие, и их текущее состояние. Но прокликать все кнопки сейчас на ней - это лишнее. Поэтому мы просто переходим в личные сообщение методом PageObject’a .enter_messages()
Такие проверки для каждого PageObject можно помещать в конструктор. Например, так:

class MessagesPage(BasePage, MessagesPageLocators):
    """
     Страница личных сообщений

     Содержит:
     - список диалогов
     - поиск по пользователям для создания нового диалога
     - поле с чатом текущего собеседника
    """

    def __init__(self, driver, url=None):
        super().__init__(driver, url)
        if not url and 'messages' not in self.url:
            self.driver.get(self.DEFAULT_HOST + '/messages')
        self.validate_header_elements_are_present()
        self.validate_messenger_elements_are_present()

Здесь проверка компонентов страницы описана в методах validate_header_elements_are_present() и validate_messenger_elements_are_present(). Т.е. фактически перед тем, как что-то вызывать на текущем pageobject - мы можем легко взять и проверить его состояние. помимо самих проверок, такие методы в конструкторе могут гарантировать, что вызов других методов на странице не будет вызывать проблем, т.к. страница точно загрузилась ( в валидаторах-методах описан перечень элементов, где каждый находится через WebDriverWait)

4 лайка

Большое спасибо, что уделили столько времени и все мне рассказали о бъяснили! :smiley:
Идея действительна очень хорошая! Попробую реализовать в своих тестах такую структуру!

Это все можно элементарно реализовать с помощью фикстур.

Создаете фикстуру с scope=“class”, где описываете шаги “Захожу на сайт -> перешел в раздел -> вижу форму”. Эта фикстура выполниться один раз на класс, в котором у Вас будут написаны различные тесты, или один parametrized тест, в дата провайтере которого будет парситься файл для различных сценариев проверки (для примера).

Никакого толку от выполнения “Захожу на сайт -> перешел в раздел -> вижу форму” в каждом тесте нет, тестировать нужно функционал, а не навигацию по сайту.