QA Interview Hub · База знань · Тести

Патерни

Facade, Steps і App Actions

Facade і бізнес-кроки

Facade дає простий інтерфейс до складної підсистеми. У тестах це шар "кроків", який поєднує кілька Page Objects і API-викликів в одну бізнес-дію: "оформити переказ", "пройти KYC".

export class TransferSteps {
  constructor(
    private readonly accounts: AccountsPage,
    private readonly transfer: TransferPage,
  ) {}

  async sendMoney(from: string, to: string, amount: number) {
    return test.step(`send ${amount} from ${from} to ${to}`, async () => {
      await this.accounts.open(from);
      await this.accounts.clickTransfer();
      await this.transfer.fill(to, amount);
      await this.transfer.confirm();
    });
  }
}

test.step групує дії у звіті і трейсі, тому падіння видно як "send 100 from A to B", а не як клік без контексту. test.step повертає значення колбека. Опція { box: true } показує місце помилки в рядку виклику кроку, а не всередині.

Коли НЕ варто: ховати у Facade те, що тест і перевіряє. Якщо тест про валідацію форми переказу, він має працювати з TransferPage напряму. Facade потрібен для передумов і повторюваних наскрізних шляхів.

Типова помилка: шари Test -> Steps -> Actions -> Pages -> Components, де кожен шар просто прокидає виклик. Це не абстракція, а непрямість, яку важко дебажити.

На співбесіді: Facade — це зазвичай клас кроків, що поєднує кілька сторінок в одну бізнес-дію, загорнуту в test.step для звіту. Його використовують для передумов, а те, що перевіряє тест, лишають видимим у самому тесті.

App Actions: передумови без UI

App Actions (термін популяризував Cypress) означає готувати стан через API, БД або сам застосунок, а не клікати UI. Наприклад, логін через UI в кожному тесті повільний і крихкий, хоча тест про зовсім інше.

У Playwright для цього є кілька механізмів:

  • storageState. Setup-проєкт логіниться один раз і зберігає cookies і localStorage у файл, а інші проєкти підключають його через use: { storageState }.
  • Фікстура request для створення даних через API перед тестом.
  • page.route для підміни відповідей бекенду.
// auth.setup.ts
import { test as setup } from '@playwright/test';

setup('authenticate', async ({ request }) => {
  await request.post('/api/login', {
    data: { email: 'qa@test.com', password: process.env.QA_PASS },
  });
  await request.storageState({ path: '.auth/user.json' });
});

У конфігу цей setup оголошується як окремий проєкт, а основний проєкт вказує його в dependencies і використовує storageState: '.auth/user.json'.

Коли НЕ варто: для тесту самого логіну, бо там UI і є предметом перевірки. Також треба мати хоча б кілька E2E, що проходять ключовий шлях повністю через UI.

На співбесіді: Передумови я готую через API і storageState, а не через UI, бо це в рази швидше і стабільніше. Через UI проходжу лише те, що тест реально перевіряє, плюс кілька наскрізних smoke-сценаріїв.