Привет коллеги. Надеюсь, у тебя все хорошо.

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

Почему JavaScript?

Ответ прост. JavaScript/TypeScript стал очень популярным не только в сообществе разработчиков, но в наши дни многие QA используют его для разработки решений для автоматизации тестирования и написания автоматизированных тестов/скриптов.

В чем разница?

Концептуально разницы нет. Все принципы одинаковы. Разница только в том, как это можно реализовать на языках JavaScript/Typescript. Именно поэтому я хочу назвать эту статью второй частью. Надеюсь, это также поможет мне лучше адаптироваться к реализациям JavaScript/TypeScript.

Пойдем!

Творческие шаблоны

Порождающие шаблоны проектирования — это набор шаблонов проектирования, которые имеют дело с механизмами создания объектов, пытаясь создать объекты способом, подходящим для ситуации. Как и в C# или Java, большинство конструктивных шаблонов проектирования можно использовать при разработке решений для автоматизации тестирования.

Синглтон

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

В этом примере мы определяем класс APIClient, реализующий шаблон Singleton. Статический метод getInstance обеспечивает создание только одного экземпляра класса APIClient и возвращает этот экземпляр. Публичные методы, такие как get и post, затем можно использовать для выполнения запросов к API.

Есть и другие подобные вещи, которые мы можем реализовать с помощью Singleton, например классы DataBase или Configuration.

В любом случае, вы должны помнить, что если вы не будете осторожны, шаблон Singleton также может сделать ваш код менее гибким и трудным для рефакторинга. Обязательно взвесьте преимущества и недостатки использования шаблона Singleton в своем решении для автоматизации тестирования и используйте его только тогда, когда это имеет смысл для вашего конкретного случая использования.

Заводской метод

Шаблон Factory Method предоставляет интерфейс для создания объектов без указания их конкретных классов.

Давайте представим, что у нас есть несколько классов TestData, оба реализуют интерфейс TestData. Одни тестовые данные будут использоваться для регистрации, а другие — для входа в систему.

Чтобы избежать создания определенных классов, мы можем создать класс TestDataFActory с методом, который создает конкретную реализацию тестовых данных.

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

Примеров гораздо больше. Одним из них является создание PageFactory. Я не собираюсь углубляться в реализацию PageObjects. Мы обсудим их чуть позже.

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

Абстрактная фабрика

Шаблон Abstract Factory предоставляет интерфейс для создания семейств связанных или зависимых объектов без указания их конкретных классов.

Предположим, нам нужно работать как с SQL, так и с не-SQL базами данных.

В этом примере мы определяем два разных семейства связанных объектов: объекты базы данных SQL и MongoDB. Мы также определяем DatabaseFactory интерфейс, предоставляющий фабричные методы для создания как DatabaseConnection, так и DatabaseQuery объектов.

Затем реализуйте два семейства объектов, используя классы SqlDatabaseFactory, MongoDatabaseFactory, SqlDatabaseConnection, SqlDatabaseQuery, MongoDatabaseConnection и MongoDatabaseQuery. Эти классы предоставляют конкретные реализации для создания объектов SQL или MongoDB DatabaseConnection и DatabaseQuery.

Семейство SQL:

Семейство Монго:

В разделе использования мы создаем экземпляры классов SqlDatabaseFactory и MongoDatabaseFactory и используем их фабричные методы для создания объектов DatabaseConnection и DatabaseQuery. Это позволяет нам создавать объекты базы данных из любого семейства баз данных без указания их конкретных типов.

Если вы хотите добавить поддержку нового типа базы данных, вы должны просто создать новые классы, реализующие интерфейсы DatabaseConnection, DatabaseQuery и DatabaseFactory.

Строитель

Шаблон Builder отделяет построение сложного объекта от его представления, позволяя одному и тому же процессу построения создавать различные представления.

Один из вариантов — создавать сложные API-запросы более модульным и удобным для сопровождения способом.

В этом примере мы определяем объект ApiRequest, который содержит URL-адрес, метод, заголовки и необязательное тело. Мы также определяем интерфейс ApiRequestBuilder, предоставляющий методы для установки различных свойств объекта ApiRequest, и класс BasicApiRequestBuilder, реализующий этот интерфейс.

Затем мы используем класс BasicApiRequestBuilder для создания сложного объекта ApiRequest более модульным и удобным для сопровождения способом. Мы связываем различные методы конфигурации объекта Builder и, наконец, вызываем метод build для создания объекта ApiRequest. Затем мы используем функцию fetch для отправки запроса API с настроенными параметрами.

Использование шаблона Builder в вашем решении для автоматизации тестирования может помочь вам создавать сложные тестовые объекты более модульным и удобным в сопровождении способом. Вы можете легко добавлять новые разновидности или вариации тестовых объектов, реализуя новые классы построителей, которые соответствуют вашему интерфейсу.

Опытный образец

Шаблон Prototype создает новый объект путем клонирования существующего объекта, что позволяет избежать накладных расходов на создание объектов с нуля.

Предположим, вам нужно создать объекты TestData. Но вы же не хотите каждый раз вводить одни и те же данные.

В этом примере мы определяем интерфейс TestData, предоставляющий некоторые свойства объектам тестовых данных, и класс BasicTestData, реализующий этот интерфейс. Мы также определяем метод clone для класса BasicTestData a, который позволяет нам создавать новый экземпляр класса путем клонирования существующего. Мы используем тип Partial<TestData>, чтобы указать, что не все свойства должны быть предоставлены.

В разделе Usage мы создаем исходный объект TestData и клонируем его с частичным объектом тестовых данных, который изменяет свойство name. Объект clonedData имеет те же свойства, что и объект originalData, за исключением обновленного свойства name.

Использование этой реализации шаблона прототипа в вашем решении для автоматизации тестирования JavaScript или TypeScript может позволить вам клонировать существующие тестовые объекты с возможностью внесения изменений. Это может быть полезно, когда вы хотите создать новые тестовые объекты, которые похожи, но отличаются некоторыми свойствами.

Структурные модели

Структурные шаблоны проектирования — это категория шаблонов проектирования в разработке программного обеспечения, которые фокусируются на отношениях между объектами и на том, как объекты могут быть объединены в более крупные структуры. Они полезны для разработки программных систем, которые являются гибкими, расширяемыми и удобными в сопровождении.

Объект страницы

Page Object — это хорошо известный шаблон проектирования в автоматизации тестирования, который можно применять в решениях для автоматизации тестирования JavaScript/TypeScript, чтобы упростить и поддерживать тесты автоматизации, а также сделать их более модульными, повторно используемыми и удобочитаемыми.

Объект страницы — это объектно-ориентированный класс, который представляет веб-страницу или раздел страницы и содержит логику и элементы, связанные со страницей, а также связанные с ней действия или методы. Основная идея шаблона Page Object состоит в том, чтобы определить единый источник правды для каждой страницы приложения, где все операции, связанные с этой страницей, могут быть доступны и легко изменены.

В этом примере мы создаем объект страницы для страницы входа в систему нашего приложения, при этом экземпляр страницы передается в качестве аргумента конструктора. Мы также объявляем и инициализируем частные переменные экземпляра для элементов страницы, связанных с входом в систему, userNameInput, passwordInput и loginButton.

В методе loginWith мы используем Playwright API для взаимодействия с элементами страницы и выполнения действия входа.

В самовызывающейся функции async мы запускаем браузер Chromium и создаем новый контекст и новую страницу. Мы переходим на страницу входа, создаем экземпляр класса LoginPage и вызываем метод loginWith для входа в приложение с заданными именем пользователя и паролем.

Наконец, вы можете добавить сюда свой код утверждения, чтобы подтвердить, был ли вход успешным.

Этот пример — простая демонстрация того, как использовать объекты страницы в Playwright с TypeScript. Используя Page Objects в своих тестах Playwright, вы можете улучшить модульность, удобство сопровождения и удобочитаемость вашего тестового кода.

Адаптер

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

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

Чтобы сделать класс LegacyLoginPage совместимым с нашим интерфейсом LoginPage, мы написали класс LoginPageAdapter, который реализует интерфейс LoginPage и использует методы класса LegacyLoginPage для аутентификации пользователя.

Теперь мы можем использовать класс LoginPageAdapter для входа в приложение с объектно-ориентированным интерфейсом LoginPage без изменения кода LegacyLoginPage.

Таким образом, шаблон разработки тестов адаптера в решениях для автоматизации тестирования JavaScript/TypeScript упрощает тестирование, предоставляя унифицированный API для тестирования различных компонентов приложения. Создав адаптер, соединяющий несовместимые интерфейсы, вы можете легко интегрировать различные компоненты таким образом, чтобы они могли беспрепятственно взаимодействовать. Этот шаблон упрощает тестирование различных частей системы с помощью одних и тех же инструментов или сред тестирования и экономит время и усилия в долгосрочной перспективе.

Фасад

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

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

Класс ApplicationFacade использует классы LoginPage и HomePage для предоставления упрощенного интерфейса для входа в систему и получения текста приветствия. Он абстрагируется от сложностей взаимодействия с различными компонентами приложения и обеспечивает более краткий и удобочитаемый способ выполнения теста.

Теперь мы можем использовать класс ApplicationFacade для выполнения тестового примера, не беспокоясь о базовых деталях классов LoginPage и HomePage.

Прокси

Шаблон проектирования прокси-теста — это структурный шаблон, предоставляющий суррогатный объект, который выступает в качестве заполнителя для другого объекта. Прокси-объект контролирует доступ к реальному объекту, позволяя создавать его только при необходимости.

В решениях для автоматизации тестирования JavaScript/TypeScript шаблон прокси может применяться для ограничения взаимодействия с объектом или классом, предоставляя дополнительные функциональные возможности без изменения кода объекта.

В этом примере у нас есть интерфейс IIpAddress и конкретный класс IpAddress, который его реализует. Класс IpAddress извлекает IP-адрес пользователя с помощью вызова REST API https://ipinfo.io/json.

Затем у нас есть класс IpAddressProxy, который также реализует интерфейс IIpAddress и действует как прокси, управляя доступом к фактическому объекту IpAddress. Класс IpAddressProxy проверяет, был ли уже кэширован IP-адрес пользователя, и извлекает его, если это не так. В противном случае он возвращает кэшированный IP-адрес.

Наконец, у нас есть самовызываемая асинхронная функция, в которой мы создаем экземпляр класса IpAddressProxy и делаем два запроса IP-адреса пользователя. Первый запрос приводит к вызову фактического объекта IpAddress, а второй запрос извлекает кэшированный IP-адрес.

Таким образом, шаблон прокси-тестирования полезен в решениях для автоматизации тестирования, когда вы хотите контролировать доступ к объекту, добавить проверку безопасности или производительности или отложить создание объекта. Шаблон предоставляет прокси-объект, который ведет себя как реальный объект, но добавляет дополнительные функции без изменения своего кода.

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

Декоратор

Шаблон Decorator можно использовать в автоматизации тестирования для добавления или изменения существующего поведения метода без изменения самого метода.

В этом примере у нас есть интерфейс ILoginPage и конкретный класс LoginPage, который его реализует. Класс LoginPage предоставляет простую реализацию входа в систему, при которой пользователь входит в систему, используя имя пользователя и пароль.

Затем у нас есть класс LoginPageDecorator, который также реализует интерфейс ILoginPage и украшает класс LoginPage. LoginPageDecorator добавляет сообщение до и после фактического процесса входа в систему, обеспечивая дополнительное поведение без изменения кода LoginPage.

Наконец, у нас есть самовызываемая асинхронная функция, в которой мы создаем экземпляры классов LoginPage и LoginPageDecorator и вызываем метод loginWith для них обоих. Первый вызов метода loginWith регистрирует пользователя с помощью простого LoginPage, а второй вызов регистрирует пользователя с помощью украшенного LoginPageDecorator.

Таким образом, шаблон проектирования тестов Decorator полезен в решениях для автоматизации тестирования, когда вы хотите добавить поведение, ведение журнала или обработку исключений к объекту без изменения его кода. Шаблон предоставляет объект декоратора, который инкапсулирует исходный объект и добавляет дополнительные функциональные возможности.

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

Поведенческие модели

Поведенческие шаблоны проектирования — это категория шаблонов проектирования в программной инженерии, которые сосредоточены на связи и взаимодействии между объектами, чтобы помочь спроектировать сложное, последовательное поведение в вашей системе. Эти шаблоны сосредоточены на том, как объекты взаимодействуют друг с другом, как они сотрудничают для достижения общих целей и как они взаимодействуют в сложных системах.

Шаблон состояния

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

В этом примере у нас есть класс LoginPage, представляющий страницу входа в веб-приложение.

Класс LoginPage имеет переменную состояния, которая начинается с LoggedOutState.

У нас также есть абстрактный класс LoginPageState, который определяет интерфейс для всех состояний, которые может иметь LoginPage.

Каждый класс состояния по-разному реагирует на методы setUsername(), setPassword() и clickSignInButton(). При вызове метода состояние изменяется на новое с помощью метода setState().

Это позволяет нам легко обрабатывать различные состояния в классе LoginPage без необходимости писать сложные условные операторы. Классу LoginPage нужно знать только свое текущее состояние, а классы состояния обрабатывают все остальное.

Теперь вы можете получить доступ к состоянию и понять статус нашей Страницы.

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

Шаблон команды

Шаблон команды может быть полезен в решениях для автоматизации тестирования, когда вам нужно инкапсулировать запросы или действия в виде объектов. Такой подход позволяет параметризовать клиентов с разными запросами для выполнения в другое время, а также поддерживать операции, которые нельзя выполнить. Например, при выполнении тестовых случаев каждый тестовый пример может быть инкапсулирован как объект и выполняться на основе его параметров.

Во-первых, давайте создадим интерфейс, который будет контрактом для всех наших команд API:

Затем создадим методы API, реализующие этот интерфейс. Для нашего примера я просто создам функции UserCreate и UserGet. В реальной жизни у него может быть больше методов.

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

В этом примере CommandManager поддерживает список команд и предоставляет методы для добавления команд, их выполнения и отмены. Когда вы вызываете commandManager.execute(), он будет перебирать каждую команду, вызывать метод execute(), сохранять результаты в массиве и добавлять выполненные команды в обратном порядке в стек отмены. Когда вы вызываете commandManager.undo(), он берет последние выполненные команды из стека отмены и вызывает их метод undo(), чтобы отменить их действие.

Теперь вы можете создать свой тестовый пример, определив каждую команду и добавив ее в CommandManager

Здесь мы инициализируем наш диспетчер команд и добавляем наши команды REST API.

Наконец, мы можем выполнять команды по порядку

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

В конце концов

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