6. Продолжаем разрабатывать автотесты - qa-guru/knowledge-base GitHub Wiki
Самодокументация кода
Сложный и нечитаемый код, который не относится к тестовой логике, необходимо самодокументировать. Это помогает упростить понимание кода для других членов команды и скрыть те его части, которые не относятся к самому тесту, а только помогают его реализовать.
Для таких случаев есть два вида самодкументирования:
- Если код являет собой действие, то его необходимо вынести в отдельную функцию;
- Если код являет собой объект или сущность, то его следует вынести в переменную.
Принцип DRY (Don’t repeat yourself)
Принцип DRY гласит, что если есть высокий риск изменений, то дубликаты в коде необходимо убирать. К примеру, если мы обращаемся к какому-нибудь селектору в коде несколько раз, то в один момент может случиться, что разработчики изменят его. В таком случае придётся вручную менять этот селектор во всём коде теста. Поэтому следует избегать таких случаев.
Варианты создания функции
У нас есть два варианта создать функцию из select
. Мы можем передавать селектор параметром, а можем передавать полный элемент.
С помощью элемента:
def select(element: SeleneElement, /, *, option: str):
element.perform(command.js.scroll_into_view).click()
browser.all('[id^=react-select-][id*=-option]').element_by(have.exact_text(option)).click()
select(browser.element('#state'), option='Uttar Pradesh')
select(browser.element('#city'), option='Lucknow')
С помощью селектора:
def select_(selector: str, /, *, option: str):
browser.element(selector).perform(command.js.scroll_into_view).click()
browser.all('[id^=react-select-][id*=-option]').element_by(have.exact_text(option)).click()
select_('#state', option='Uttar Pradesh')
select_('#city', option='Lucknow')
Модульная парадигма
Если мы будем реализовывать и вызывать функции в одном файле, то рано или поздно файл станет очень большим и во всём это разнообразии можно будет легко потеряться. Для предотвращения этого придумали модульную парадигму, которая подразумевает под собой объединение функций в отдельные модули, которые можно подключать и вызывать в других местах проекта.
К примеру, на тестируемой странице очень много элементов, которые отвечают за ввод текста и выбор вариантов. Их много, они все разные и для каждого нужны отдельные функции. Поэтому мы можем создать в проекте отдельный пакет и создать в нём отдельный файл с реализацией этих функций.
Как создать пакет в PyCharm
В главном пакете в корне проекта нажимаем правой кнопкой мыши. Выбираем «New», а затем «Python Package». Пакету даем осмысленное название, которое отражало бы его содержимое и назначение. К примеру, для пакета, в котором находится реализация выбора элементов на странице, можно дать название «controls».
Как создать модуль в пакете
В корне пакета нажимаем правой кнопкой мыши. Выбираем «New», затем «Python File». Также даём осмысленное название. К примеру, мы создаём модуль для работы с селектами, поэтому и называем его «select»
Теперь мы можем вынести реализацию функций в созданный файл. после этого в главном файле с тестами необходимо импортировать созданный модуль, чтобы появилась возможность вызывать функции из модуля. Делается это следующим образом:
from demoqa_tests.controls.select import function_one, function_two
Необходимо стараться скрывать из главного тестового файла функции, технические подробности, особенности UI-структуры тестируемой страницы и стараться держать в файле только реализацию тест-кейсов.
Объектно-ориентированная парадигма
У модульно парадигмы есть плюсы, которые помогают навести порядок в коде и сделать его более читаемым. Но есть и минусы, когда к одному пакету обращается слишком много раз и Python приходится запускать по несколько процессов. К примеру, если абстрагироваться и представить, что пакеты, которые мы создаём, представляют собой ящик с инструментами, то в момент работы кода к один ящик пытаются поделить сразу несколько исполнителей. Логично, что одного ящика не хватает сразу нескольким работникам.
В этот момент приходит на помощью объектно-ориентированная парадигма с классами, которые представляют собой фабрики по изготовлению ящиков, которая выпускает новый ящик инструментов каждый раз, когда появляется в этом необходимость.
Как создать класс в Python
К примеру, в коде нашего модуля было следующее:
element: SeleneElement = ...
def add(from_: str, /, *, autocomplete: Optional[str] = None):
element.type(from_)
browser.all(
'.subjects-auto-complete__option'
).element_by(have.text(autocomplete or from_)).click()
Если мы переделаем модульную парадигму в объектно-ориентированную, то код будет выглядеть следующим образом:
class TagsInput:
def __init__(self):
self.element: SeleneElement = ...
def add(self, from_: str, /, *, autocomplete: Optional[str] = None):
element.type(from_)
browser.all(
'.subjects-auto-complete__option'
).element_by(have.text(autocomplete or from_)).click()
Как этим пользоваться
Теперь у нас есть фабрика, которая может изготавливать новые ящики с инструментами. К примеру, если нам понадобился новый ящик, то в коде это будет выглядеть следующим образом:
tags_input = TagsInput()
Если понадобится ещё один, то:
tags_input = TagsInput()
tags_input2 = TagsInput()