Использование класса в тесте

Урок: Использование класса в тесте

Введение

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

  • сначала научились создавать классы;
  • затем добавили методы;
  • потом превратили классы в Page Object;
  • и сделали методы асинхронными.

Теперь осталось самое главное — научиться использовать всё это в реальных тестах.

Ниже в песочнице теории нет @playwright/test, поэтому мы имитируем минимальный test и page, чтобы код выполнялся и печатал шаги.


Как выглядел сценарий без Page Object

const page = {
  goto: async (url) => console.log('goto', url),
  fill: async (selector, value) => console.log('fill', selector, value),
  click: async (selector) => console.log('click', selector),
};

(async () => {
  await page.goto('https://example.com/login');
  await page.fill('#login', 'user');
  await page.fill('#password', '123');
  await page.click('#submit');
})();

Когда тестов много, селекторы и шаги дублируются.


Page Object и «тест» в одном фрагменте

const page = {
  goto: async (url) => console.log('goto', url),
  fill: async (selector, value) => console.log('fill', selector, value),
  click: async (selector) => console.log('click', selector),
};

class LoginPage {
  constructor(page) {
    this.page = page;
    this.loginInput = '#login';
    this.passwordInput = '#password';
    this.submitButton = '#submit';
  }

  async login(login, password) {
    await this.page.fill(this.loginInput, login);
    await this.page.fill(this.passwordInput, password);
    await this.page.click(this.submitButton);
  }
}

async function test(name, fn) {
  console.log('TEST:', name);
  await fn({ page });
}

(async () => {
  await test('логин пользователя', async ({ page }) => {
    const loginPage = new LoginPage(page);
    await page.goto('https://example.com/login');
    await loginPage.login('user', '123');
  });
})();

Создание объекта и вызов метода

const page = {
  fill: async (s, v) => console.log('fill', s, v),
  click: async (s) => console.log('click', s),
};

class LoginPage {
  constructor(page) {
    this.page = page;
    this.loginInput = '#login';
    this.passwordInput = '#password';
    this.submitButton = '#submit';
  }

  async login(login, password) {
    await this.page.fill(this.loginInput, login);
    await this.page.fill(this.passwordInput, password);
    await this.page.click(this.submitButton);
  }
}

(async () => {
  const loginPage = new LoginPage(page);
  await loginPage.login('user', '123');
})();

Сравнение стиля

Низкоуровнево (всё на page):

const page = {
  fill: async (s, v) => console.log('fill', s, v),
  click: async (s) => console.log('click', s),
};

(async () => {
  await page.fill('#login', 'user');
  await page.fill('#password', '123');
  await page.click('#submit');
})();

Через Page Object (смысл тот же, читается как сценарий):

const page = {
  fill: async (s, v) => console.log('fill', s, v),
  click: async (s) => console.log('click', s),
};

class LoginPage {
  constructor(page) {
    this.page = page;
    this.loginInput = '#login';
    this.passwordInput = '#password';
    this.submitButton = '#submit';
  }

  async login(login, password) {
    await this.page.fill(this.loginInput, login);
    await this.page.fill(this.passwordInput, password);
    await this.page.click(this.submitButton);
  }
}

(async () => {
  const loginPage = new LoginPage(page);
  await loginPage.login('user', '123');
})();

Пример с проверкой (учебный expect)

const page = {
  goto: async (url) => console.log('goto', url),
  fill: async (s, v) => console.log('fill', s, v),
  click: async (s) => console.log('click', s),
};

const expect = {
  async toHaveURL(part) {
    console.log('assert URL содержит', part);
  },
};

class LoginPage {
  constructor(page) {
    this.page = page;
    this.loginInput = '#login';
    this.passwordInput = '#password';
    this.submitButton = '#submit';
  }

  async login(login, password) {
    await this.page.fill(this.loginInput, login);
    await this.page.fill(this.passwordInput, password);
    await this.page.click(this.submitButton);
  }
}

(async () => {
  const loginPage = new LoginPage(page);
  await page.goto('https://example.com/login');
  await loginPage.login('user', '123');
  await expect.toHaveURL('/dashboard');
})();

Ошибка: забыли await у async-метода

const page = {
  fill: async (s, v) => console.log('fill', s, v),
  click: async (s) => console.log('click', s),
};

class LoginPage {
  constructor(page) {
    this.page = page;
    this.loginInput = '#login';
    this.passwordInput = '#password';
    this.submitButton = '#submit';
  }

  async login(login, password) {
    await this.page.fill(this.loginInput, login);
    await this.page.fill(this.passwordInput, password);
    await this.page.click(this.submitButton);
  }
}

(async () => {
  const loginPage = new LoginPage(page);
  const p = loginPage.login('user', '123');
  console.log('без await снаружи — это Promise?', p instanceof Promise);
  await p;
})();

Правильно всегда писать await loginPage.login(...) при последовательном сценарии.


Импорт в реальном проекте

В Playwright-проекте класс обычно подключают из файла:

// В реальном репозитории:
// import { LoginPage } from '../pages/LoginPage';

console.log('Структура: pages/LoginPage + tests/*.spec.ts');

Итоговое понимание

Тест описывает сценарий, Page Object — как именно страница выполняет шаги.

Ты больше не думаешь только «как кликнуть», а «что делает пользователь» — и вызываешь методы страницы.

Именно так строятся реальные проекты на Playwright.