🔍 Вивчення шаблону PageObject для автоматизованого тестування 🤖

Ви тестувальник програмного забезпечення, який прагне оптимізувати свої автоматизовані тести та зробити їх зручнішими та читабельнішими? Якщо так, то шаблон PageObject може бути вашою секретною зброєю! 🛠️
Шаблон PageObject — це шаблон проектування, який покращує організацію та структуру ваших тестів автоматизації. Він абстрагує інтерфейс користувача на окремі класи або об’єкти, роблячи ваш тестовий код чистішим, модульнішим і простішим у обслуговуванні. Розглянемо цей шаблон на простому прикладі за допомогою Playwright і TypeScript.
У цій публікації я крок за кроком, починаючи з нуля, розповім вам, як створити свій перший проект Playwright. Я покажу вам, як створити об’єктно-орієнтований шаблон Page і як написати свій перший тест за допомогою об’єкта Page. Почнемо з базового налаштування.
Крок 1: Базове налаштування
Node.js
Перш за все, переконайтеся, що на вашій робочій станції встановлено Node.js. Відкрийте командний рядок і виконайте таку команду:
node -v
Результат має відображати версію Node.js, встановлену на вашій локальній машині. Якщо цього не сталося, переконайтеся, що встановили Node.js на своєму комп’ютері.
Playwright
Щоб отримати докладнішу інформацію про різні способи інсталяції Playwright на вашій локальній машині, ви можете звернутися до документації. Однак я продемонструю базовий підхід із використанням пакетів npm.
Створення каталогу
Щоб створити кореневий каталог для вашого проекту за допомогою командного рядка, виконайте такі дії:
- Відкрийте командний рядок.
- Перейдіть до розташування, де ви хочете створити каталог проекту. Ви можете скористатися командою cd, щоб змінити поточний робочий каталог.
- Створіть новий каталог за допомогою команди mkdir. Наприклад, щоб створити каталог із назвою "my-dramaturg-project", ви можете використати:
mkdir my-playwright-project
Це створить каталог із вказаною назвою у вашому поточному місці. Потім ви можете перейти до цього каталогу за допомогою команду cd, щоб розпочати налаштування вашого проекту Playwright у ньому.
cd .\my-playwright-project\
Встановлення Playwright
Ви можете встановити Playwright за допомогою команди npm. Виконайте таку команду в командному рядку:
npm init playwright@latest
Дотримуйтесь інструкцій, які з’являються під час виконання цієї команди, щоб завершити процес інсталяції. Якщо ви вибрали варіант false для встановлення браузерів і хочете встановити Chromium, виконайте таку команду:
npx playwright install chromium
Це встановить браузер Chromium для вашого проекту Playwright.
Налаштування Playwright проекту
Давайте виконаємо просту конфігурацію для нашого проекту Playwright. Перейдіть до файлу playwright.config.ts і внесіть такі зміни:
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : 1,
reporter: 'html',
use: {
trace: 'on-first-retry',
},
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
],
});
У цьому оновленому конфігураційному файлі ми спростили його, видаливши коментарі, наразі встановивши лише одного робочого елемента (пізніше ми ввімкнемо паралельну роботу), і видалення непотрібних проектів, які ми не будемо використовувати. Ці зміни мають зробити вашу конфігурацію Playwright більш простою.
Тепер ви можете запустити свої перші тести за допомогою такої команди:
npx playwright test
Ця команда виконає ваші тести Playwright на основі конфігурації, яку ви встановили у файлі playwright.config.ts.
Running 2 tests using 1 worker
2 passed (10.2s)
Крок 2: Шаблон об’єкта сторінки
Давайте розглянемо структуру за замовчуванням, яку пропонує Playwright із коробки. Відкрийте файл ./tests/example.spec.ts, щоб побачити, як a типовий тест для драматурга структурований.
import { test, expect } from '@playwright/test';
test('has title', async ({ page }) => {
await page.goto('https://playwright.dev/');
// Expect a title "to contain" a substring.
await expect(page).toHaveTitle(/Playwright/);
});
test('get started link', async ({ page }) => {
await page.goto('https://playwright.dev/');
// Click the get started link.
await page.getByRole('link', { name: 'Get started' }).click();
// Expects page to have a heading with the name of Installation.
await expect(page.getByRole('heading', { name: 'Installation' })).toBeVisible();
});
Структура тесту за замовчуванням у Playwright може мати деякі недоліки:
- Дублювання коду: часто можна побачити повторюваний код для взаємодії з тими самими елементами в кількох тестах, що може призвести до проблем з обслуговуванням.
- Крихкі тести: якщо локатор або поведінка елемента змінюється, вам потрібно оновити його в кількох місцях, що робить тести більш крихкими та схильними до зламів.
- Обмежена можливість повторного використання: існує обмежена можливість повторного використання коду для взаємодії з елементами, що ускладнює спільний доступ до логіки взаємодії між різними тестами.
Ось тут і з’являється шаблон об’єкта сторінки, щоб вирішити ці проблеми. Шаблон об’єкта сторінки надає кілька переваг:
- Абстракція: вона абстрагує взаємодії інтерфейсу користувача та локатори в окремі класи, роблячи тести більш читабельними та зручними для обслуговування.
- Повторне використання коду: Ви можете повторно використовувати ці класи Page Object у кількох тестах, зменшуючи дублювання та покращуючи послідовність.
- Легке обслуговування: якщо локатор або поведінка елемента змінюється, вам потрібно оновити його лише в одному місці, у класі Page Object, який автоматично поширюватиметься зміна всіх тестів, які використовують цей об’єкт сторінки.
- Покращена співпраця: команди можуть працювати ефективніше, розділяючи обов’язки між тестовим кодом і кодом Page Object, що дозволяє паралельну розробку.
У наступних кроках ми розглянемо, як застосувати шаблон об’єкта Page у ваших тестах Playwright, щоб реалізувати ці переваги.
Реалізація об'єкта сторінки
Щоб ефективно реалізувати шаблон об’єкта сторінки, дуже важливо спочатку зрозуміти веб-програму, яку ви будете автоматизувати. Давайте переглянемо ваш демо-сайт маєте намір охопити своїми тестами автоматизації, які є https://demo.playwright.dev/todomvc/#/.
Під час відвідування веб-сайту здається, що це програма TodoMVC, поширений приклад для тестування веб-автоматизації. Ось деякі з елементів і функцій ви можете розглянути тестування:
- Поле введення Todo: тут ви можете вводити нові завдання.
- Кнопка «Додати»: натискання цієї кнопки має додати введене завдання до списку.
- Список завдань: це список завдань, у якому ви можете позначити завдання як завершені або видалити їх.
- Прапорець: ви можете встановити або зняти прапорець біля завдання, щоб позначити його як завершене або невиконане.
- Кнопка «Очистити виконане». Ця кнопка дозволяє очистити всі виконані завдання зі списку.
- Лічильник: показує кількість завдань у списку.
- Фільтри: веб-сайт пропонує фільтри для перегляду всіх, активних або виконаних завдань.
Щоб реалізувати шаблон об’єкта сторінки, ви створите окремі класи об’єктів сторінки для кожного з цих елементів або функцій. Кожен клас Page Object має інкапсулювати взаємодії та методи, пов’язані з цією конкретною частиною сторінки.
Цей структурований підхід зробить ваші тести більш модульними, придатними для обслуговування та багаторазового використання. У наступних кроках ми можемо розглянути, як створити Page Object класи для цих елементів і пишіть тести за допомогою шаблону Page Object.
Основна сторінка
Створення базового класу сторінки є хорошою практикою під час реалізації шаблону об’єкта сторінки. Цей базовий клас сторінки міститиме загальні методи та елементи які спільно використовуються на кількох сторінках вашої веб-програми. Почнемо зі створення базової сторінки для вашої програми TodoMVC.
import { Page, Response } from '@playwright/test';
class BasePage {
private readonly url: string = 'https://demo.playwright.dev/todomvc/#/'
constructor(protected readonly page: Page) {}
get title(): Promise<string> {
return this.page.title();
}
get pageUrl(): string {
return this.page.url();
}
goto = (): Promise<Response | null> => this.page.goto(this.url);
}
export default BasePage;
Тепер, коли у нас є базова сторінка, яка містить метод дії для відкриття нашої сторінки через URL-адресу, а також дві властивості для отримання заголовка сторінки та поточної URL-адреси, давайте створимо тести для перевірки заголовка та URL-адреси. Ми збираємося запровадити два тести, оскільки ми перевіряємо різні критерії.
import { test, expect } from '@playwright/test';
import BasePage from '../src/pages/base-page';
test('has title', async ({ page }) => {
const basePage = new BasePage(page);
await basePage.goto();
expect(await basePage.title).toBe('React • TodoMVC');
});
test('get current url', async ({ page }) => {
const basePage = new BasePage(page);
await basePage.goto();
expect(basePage.pageUrl).toContain('demo.playwright.dev');
});
Щоб запустити ці тести, скористайтеся командним рядком і перевірте, чи вони пройшли чи не пройшли. Сподіваюся, вони пройдуть.
Однак у нас все ще є деякі дублювання. Давайте спростимо тести, щоб уникнути надмірності. Давайте використаємо під капотом хук beforeEach, наданий Playwright.
import { test, expect } from '@playwright/test';
import BasePage from '../src/pages/base-page';
var basePage: BasePage;
test.describe('my first page object pattern tests', () => {
test.beforeEach(async ({ page }) => {
basePage = new BasePage(page);
await basePage.goto();
});
test('has title', async () => expect(await basePage.title).toBe('React • TodoMVC'));
test('get current url', async () => expect(basePage.pageUrl).toContain('demo.playwright.dev'));
});
Як ви можете бачити, наші тести тепер написані в більш стислому форматі з лише одним рядком для кожного тесту, що є покращенням порівняно з попереднім.
Створіть TodoPage
Давайте розглянемо сценарій, коли наша веб-програма складається з кількох сторінок. Щоб керувати цією складністю, кожна сторінка має бути представлена окремим класом. Наведений нижче код демонструє об’єкт Page для головної сторінки завдань.
import { Page } from "@playwright/test";
import BasePage from "../base-page";
class TodoPage extends BasePage {
constructor(page: Page) {
super(page);
}
}
export default TodoPage;
У цьому фрагменті коду ми розширюємо шаблон об’єкта сторінки, щоб створити спеціальний клас об’єкта сторінки для головної сторінки завдань у нашому веб-додатку. Це допомагає організувати наш код і забезпечує чітке розділення проблем.
Ось що відбувається в коді:
- Ми імпортуємо необхідні модулі та залежності, включаючи клас Page і ваш клас BasePage.
- Ми створюємо новий клас TodoPage, який розширює BasePage. Це дозволяє TodoPage успадковувати загальні методи та елементи з класу BasePage.
- У конструкторі ми передаємо екземпляр Page методу super() для ініціалізації BasePage.
- Розширюючи BasePage, ми можемо додавати спеціальні методи та елементи, пов’язані з головною сторінкою завдань, що полегшує написання тестів для цієї конкретної частини нашої веб-програми.
Цей підхід сприяє повторному використанню коду та зручності обслуговування, оскільки ми можемо створювати окремі класи Page Object для різних сторінок у нашій програмі, підтримуючи наш тестовий код чистим і організованим.
Створення елементів сторінки Todo
Як згадувалося раніше, добре створювати окремі класи об’єктів сторінки для кожного елемента чи функції у вашій веб-програмі. Почнемо зі створення класу Page Object для елемента "TodoApp".
import { Locator, Page } from "@playwright/test";
export class TodoApp {
private readonly newTodoItem: Locator = this.page.locator('header > input');
private readonly leftTodoItems: Locator = this.page.locator('//*[@data-testid="todo-count"]/strong');
constructor (private readonly page: Page) {}
addNewItem = async (newItem: string): Promise<void> => {
await this.newTodoItem.fill(newItem);
return this.page.keyboard.press('Enter');
}
addNewItems = async (newItems: string[]): Promise<void> => {
for (let i = 0; i < newItems.length; i++) {
const newItem = newItems[i];
await this.addNewItem(newItem);
}
}
getLeftTodoItems = async (): Promise<number> => {
return Number(await this.leftTodoItems.innerText());
}
}
export default TodoApp;
Ми створили клас Page Object для елемента "TodoApp" у нашій веб-програмі, що є чудовим способом інкапсуляції взаємодії та методів пов'язані з цим конкретним елементом. Давайте підсумуємо ключові моменти коду:
- Ми визначили клас TodoApp, який представляє елемент "TodoApp".
- Усередині конструктора ми ініціалізували два локатори, newTodoItem і leftTodoItems, які використовуються для пошуку певних елементів у «TodoApp».
- Ми створили методи для взаємодії з елементом "TodoApp":
- addNewItem дозволяє нам додати новий елемент до списку завдань, заповнивши поле введення та натиснувши Enter.
- addNewItems — це службовий метод для додавання кількох елементів шляхом повторного виклику addNewItem.
- getLeftTodoItems отримує кількість завдань, що залишилися, що корисно для перевірки, чи елементи додаються та позначаються як завершені.
Цей клас Page Object ефективно інкапсулює взаємодію з елементом "TodoApp" і забезпечує простий і модульний спосіб написання тести для цієї частини вашої веб-програми. Це хороша практика для підтримки зрозумілих і придатних для обслуговування тестів автоматизації.
Давайте включимо цей "TodoApp" до нашої "TodoPage", виконайте наведений нижче код:
import { Locator, Page } from "@playwright/test";
import BasePage from "../base-page";
import TodoApp from "./page-elements/todo-app";
class TodoPage extends BasePage {
public pageHeader: Locator = this.page.locator('h1');
public todoApp: TodoApp = new TodoApp(this.page);
constructor(page: Page) {
super(page);
}
}
export default TodoPage;
Ми успішно включили об’єкт сторінки TodoApp у нашу сторінку TodoPage. Ось короткий перелік змін у коді:
- Ми імпортували клас TodoApp Page Object.
- Ми створили загальнодоступну властивість todoApp та ініціалізували її новим екземпляром об’єкта сторінки TodoApp у конструкторі.
Завдяки цим змінам ми тепер маємо доступ до методів і елементів, визначених у класі TodoApp, із нашої TodoPage. Це робить наш код більш організованим і дотримується шаблону об’єкта сторінки, дозволяючи нам взаємодіяти з елементом «TodoApp» у контексті головної сторінки завдань.
Тест після змін
Щоб написати новий тест після змін, ви можете створити новий тестовий файл із назвою, яка закінчується на .spec.ts, щоб вказати, що це тестовий файл. Ось приклад того, як створити тест для вашої "TodoPage" за допомогою Playwright:
import { test, expect } from '@playwright/test';
import TodoPage from '../src/pages/todo-page/todo-page';
var todoPage: TodoPage;
test.describe('my first page object pattern tests', () => {
test.beforeEach(async ({ page }) => {
todoPage = new TodoPage(page);
await todoPage.goto();
});
test('add single todo item', async () => {
await todoPage.todoApp.addNewItem('newTodoItem');
const leftTodoItems = await todoPage.todoApp.getLeftTodoItems();
expect(leftTodoItems).toBe(1);
});
test('add multiple todo items', async () => {
const newItems: string[] = ['newTodoItem1', 'newTodoItem2'];
await todoPage.todoApp.addNewItems(newItems);
const leftTodoItems = await todoPage.todoApp.getLeftTodoItems();
expect(leftTodoItems).toBe(newItems.length);
});
});
Ми створили тестовий файл, використовуючи правила іменування .spec.ts, і організували наші тести за допомогою хуків describe і test.beforeEach. Ця структура відповідає шаблону Page Object і робить наші тести більш організованими та модульними.
Ось короткий виклад нашого тестового коду:
- Ми імпортуємо необхідні модулі, включаючи тестування та очікування від Playwright, а також функцію опису.
- Ми створюємо екземпляр TodoPage для використання в наших тестах і використовуємо хук test.beforeEach для переходу на сторінку перед кожним тестом.
- Ми організували наші тести в блок опису, який допомагає згрупувати пов’язані тести.
- У першому тесті ми додаємо один елемент Todo, перевіряємо, чи його додано, і перевіряємо кількість завдань, що залишилися.
- У другому тесті ми додаємо кілька завдань, перевіряємо, чи їх додано, і перевіряємо кількість завдань, що залишилися.
Ця структура є чудовим способом реалізації шаблону об’єкта сторінки та гарантує, що наші тести є зручними та модульними.
Зачекай! Ми все ще маємо дубльований код у двох тестових файлах:
var basePage: BasePage;
test.beforeEach(async ({ page }) => {
basePage = new BasePage(page);
await basePage.goto();
});
and
var todoPage: TodoPage;
test.beforeEach(async ({ page }) => {
todoPage = new TodoPage(page);
await todoPage.goto();
});
Ми все ще можемо спростити його, щоб використовувати фікстури.
Тестові прилади використовуються для встановлення середовища для кожного тесту, надаючи тесту все, що йому потрібно, і нічого більше. Отже, як ми можемо спростити наші два файли зі світильниками.
Давайте створимо новий файл ./tests/fixtures/fixture.ts у нашій папці tests:
import { test as base } from '@playwright/test';
import TodoPage from '../../src/pages/todo-page/todo-page';
type Fixture = {
todoPage: TodoPage;
}
const test = base.extend<Fixture>({
todoPage: async ({ page }, use) => {
const todoPage = new TodoPage(page);
await todoPage.goto();
await use(todoPage);
},
});
export default test;
export { expect } from '@playwright/test';
Ми створили файл фікстури, щоб спростити налаштування нашого тесту, що є чудовим підходом для усунення дублювання. Давайте переглянемо зміни:
У нашому файлі ./tests/fixtures/fixture.ts:
- Ми імпортуємо тест із Playwright, щоб визначити наш тест.
- Ми визначаємо тип приладу, щоб визначити, що забезпечує прилад. У цьому випадку він надає екземпляр todoPage TodoPage.
- Ми створюємо функцію тестового розширення, яка встановлює сторінку todoPage і передає її тесту.
- Функція use використовується для надання екземпляра todoPage для тесту.
- Ми експортуємо тестовий прилад і функцію очікування з Playwright для використання в наших тестах.
Завдяки такому налаштуванню наш тестовий код спрощується. Цей підхід відповідає найкращим практикам і покращує зручність обслуговування вашого тестового коду. Давайте подивимося, як це працює.
тоді
import { test, expect } from '@playwright/test';
import BasePage from '../src/pages/base-page';
var basePage: BasePage;
test.describe('my first page object pattern tests', () => {
test.beforeEach(async ({ page }) => {
basePage = new BasePage(page);
await basePage.goto();
});
test('has title', async () => expect(await basePage.title).toBe('React • TodoMVC'));
test('get current url', async () => expect(basePage.pageUrl).toContain('demo.playwright.dev'));
});
тепер
import test, { expect } from './fixtures/fixture';
test.describe('my first page object pattern tests', () => {
test('has title', async ({ todoPage }) => expect(await todoPage.title).toBe('React • TodoMVC'));
test('get current url', async ({ todoPage }) => expect(todoPage.pageUrl).toContain('demo.playwright.dev'));
});
тоді
import { test, expect } from '@playwright/test';
import TodoPage from '../src/pages/todo-page/todo-page';
var todoPage: TodoPage;
test.describe('my first page object pattern tests', () => {
test.beforeEach(async ({ page }) => {
todoPage = new TodoPage(page);
await todoPage.goto();
});
test('add single todo item', async () => {
await todoPage.todoApp.addNewItem('newTodoItem');
const leftTodoItems = await todoPage.todoApp.getLeftTodoItems();
expect(leftTodoItems).toBe(1);
});
test('add multiple todo items', async () => {
const newItems: string[] = ['newTodoItem1', 'newTodoItem2'];
await todoPage.todoApp.addNewItems(newItems);
const leftTodoItems = await todoPage.todoApp.getLeftTodoItems();
expect(leftTodoItems).toBe(newItems.length);
});
});
тепер
import test, { expect } from './fixtures/fixture';
test.describe('my first page object pattern tests', () => {
test('add single todo item', async ({ todoPage }) => {
await todoPage.todoApp.addNewItem('newTodoItem');
const leftTodoItems = await todoPage.todoApp.getLeftTodoItems();
expect(leftTodoItems).toBe(1);
});
test('add multiple todo items', async ({ todoPage }) => {
const newItems: string[] = ['newTodoItem1', 'newTodoItem2'];
await todoPage.todoApp.addNewItems(newItems);
const leftTodoItems = await todoPage.todoApp.getLeftTodoItems();
expect(leftTodoItems).toBe(newItems.length);
});
});
Ми внесли значні покращення, використовуючи прилади для спрощення налаштування тесту. Ось як це працює:
- Ми створили фікстуру, яка встановлює екземпляр todoPage, усуваючи необхідність створювати його в кожному тесті та, таким чином, зменшуючи дублювання.
- Ми використали тестову функцію з нашого приладу в наших тестових файлах, і todoPage автоматично надається для кожного тесту.
- Файли тестування стали чистішими та більше зосередженими на фактичних сценаріях тестування, що робить їх більш читабельними та зручними для обслуговування.
Застосувавши ці зміни, ми покращили структуру наших тестів, дотримувалися найкращих практик і зробили наш тестовий код більш зручним для обслуговування. Це чудовий приклад того, як ефективно реалізувати шаблон об’єкта сторінки та оптимізувати процес тестування.
Висновок
Я надав детальний посібник із впровадження шаблону об’єкта Page в автоматизованому тестуванні за допомогою Playwright і TypeScript. Цей шаблон справді є цінним інструментом для покращення організації тестового коду, можливості повторного використання та обслуговування. Ці покрокові інструкції полегшують тестерам, особливо тим, хто вперше знайомиться з цією концепцією, зрозуміти й ефективно застосувати шаблон об’єкта сторінки.
Додавання фікстур є чудовим покращенням, зменшуючи дублювання коду та спрощуючи налаштування тесту. Такий підхід покращує ясність і придатність тестових файлів.
Цей посібник є цінним ресурсом для тих, хто хоче створити ефективні та добре структуровані автоматизовані тести за допомогою Playwright і TypeScript. Він висвітлює найкращі практики та забезпечує чіткий шлях для успішної реалізації шаблону об’єкта сторінки. Чудова робота!
Ресурси
- [1] повне репо з кодом;
- [2] TypeScript документація;
- [3] Playwright документація;
хожі публікації

Зсув тестування ліворуч: Виведення якості на передній план
Вступ: У світі розробки програмного забезпечення якість вашого продукту має першочергове значення. Традиційно тестування розглядалося як етап, який настає після розробки. Однак із застосуванням зсуву тестування вліво відбувається зміна парадигми. У цьому дописі ми дослідимо, що таке тестування зі ...

Освоєння транзакцій SQL: потужність COMMIT і ROLLBACK в управлінні базами даних
Транзакції SQL є фундаментальним аспектом керування базою даних, який відіграє вирішальну роль у підтримці цілісності даних. Серед ключових компонентів транзакцій оператор COMMIT виділяється як опора для забезпечення того, що зміни, внесені до бази даних, є постійними. У цій публікації блогу ми ...

🌟 Функціональне програмування в автоматизованому тестуванні за допомогою TypeScript і Playwright 🤖🚀 Частина II
Ласкаво просимо до другої частини нашого дослідження функціонального програмування в автоматизованому тестуванні. У цьому продовженні ми глибше заглибимося в практичну реалізацію концепцій, які вже обговорювалися досі. Ось що ви можете очікувати в цій частині: Ось покрокові інструкції щодо встан...