🚀 Tổng quan kiến trúc (Cucumber + Playwright)

.feature (Gherkin)
        ↓
Step Definitions (Cucumber)
        ↓
Playwright API (browser automation)
        ↓
Browser (Chromium/WebKit/Firefox)
        ↓
App (Angular / API)

👉 Cucumber = viết kịch bản (readable specs)
👉 Playwright = thực thi automation (powerful browser control)


📦 1. Setup project (Node.js + TypeScript)

npm init -y

npm install -D \
  @cucumber/cucumber \
  playwright \
  ts-node \
  typescript \
  @types/node

📁 2. Structure project (chuẩn production)

project/
│
├── features/
│   └── login.feature
│
├── steps/
│   └── login.steps.ts
│
├── support/
│   ├── hooks.ts
│   └── world.ts
│
├── pages/
│   └── login.page.ts
│
├── cucumber.js
└── tsconfig.json

👉 Structure này giúp scalemaintain dễ dàng trong project thật.


🧪 3. Viết file feature (Gherkin)

Feature: Login

  Scenario: Login thành công
    Given user mở trang login
    When user nhập username "admin" và password "123456"
    Then user thấy dashboard

⚙️ 4. Step Definitions (kết nối với Playwright)

import { Given, When, Then } from "@cucumber/cucumber";
import { chromium, Browser, Page } from "playwright";
import assert from "assert";

let browser: Browser;
let page: Page;

Given("user mở trang login", async () => {
  browser = await chromium.launch({ headless: false });
  page = await browser.newPage();
  await page.goto("http://localhost:4200/login");
});

When(
  "user nhập username {string} và password {string}",
  async (username, password) => {
    await page.fill("#username", username);
    await page.fill("#password", password);
    await page.click("#login-btn");
  },
);

Then("user thấy dashboard", async () => {
  await page.waitForSelector("#dashboard");
  const text = await page.textContent("#dashboard");
  assert(text?.includes("Welcome"));
  await browser.close();
});

🧠 5. Tách Page Object (Best Practice cho Senior)

👉 Không nên viết selector trực tiếp trong steps

pages/login.page.ts

import { Page } from "playwright";

export class LoginPage {
  constructor(private page: Page) {}

  async goto() {
    await this.page.goto("/login");
  }

  async login(username: string, password: string) {
    await this.page.fill("#username", username);
    await this.page.fill("#password", password);
    await this.page.click("#login-btn");
  }
}

Refactor lại Step với Page Object

import { LoginPage } from "../pages/login.page";

let loginPage: LoginPage;

Given("user mở trang login", async function () {
  this.browser = await chromium.launch();
  this.page = await this.browser.newPage();

  loginPage = new LoginPage(this.page);
  await loginPage.goto();
});

When(
  "user nhập username {string} và password {string}",
  async function (username, password) {
    await loginPage.login(username, password);
  },
);

🔄 6. Hooks (Setup / Teardown chuẩn)

support/hooks.ts

import { Before, After } from "@cucumber/cucumber";
import { chromium } from "playwright";

Before(async function () {
  this.browser = await chromium.launch();
  this.page = await this.browser.newPage();
});

After(async function () {
  await this.browser.close();
});

👉 Lợi ích:


🌍 7. World (Chia sẻ state giữa steps)

support/world.ts

import { setWorldConstructor } from "@cucumber/cucumber";
import { Browser, Page } from "playwright";

class CustomWorld {
  browser!: Browser;
  page!: Page;
}

setWorldConstructor(CustomWorld);

▶️ 8. Run test

cucumber.js

module.exports = {
  default: {
    require: ["steps/**/*.ts", "support/**/*.ts"],
    requireModule: ["ts-node/register"],
    format: ["progress"],
  },
};

Run command

npx cucumber-js

🔥 Flow thực tế (Quan trọng cho Interview)

Business viết Gherkin
        ↓
Dev map step → code
        ↓
Playwright điều khiển browser
        ↓
Test UI / API thật
        ↓
Generate report

⚠️ Sai lầm phổ biến (Senior hay hỏi)

❌ Sai ✅ Đúng
Viết selector trực tiếp trong step Dùng Page Object Pattern
Không dùng Page Object Tách riêng page classes
Test phụ thuộc nhau (state leak) Mỗi test độc lập
Không handle async properly Dùng async/await đúng cách
Selector yếu (brittle tests) Dùng data-testid hoặc stable selectors

🎯 Nâng level (Senior mindset)

1. Data-driven test

Scenario Outline: Login với nhiều data sets
  When user login với "<username>""<password>"
  Then thấy "<result>"

Examples:
  | username | password | result    |
  | admin    | 123456   | success   |
  | admin    | wrong    | error     |
  | invalid  | 123456   | error     |

2. Parallel test

→ Chạy nhiều browser cùng lúc để tăng tốc độ

3. API + UI hybrid test

→ Login bằng API → Skip UI login → Test nhanh hơn nhiều lần

4. Retry + Flaky handling

→ Cực kỳ quan trọng trong project lớn với unstable network/services


🧠 Tóm lại

Cucumber = viết spec readable cho cả team (BA, QA, Dev)
Playwright = thực thi automation mạnh mẽ, cross-browser

Kết hợp cả hai giúp bạn có:


📚 Tài liệu tham khảo