Strategy: змінна поведінка за спільним інтерфейсом
Strategy визначає сімейство взаємозамінних алгоритмів за одним інтерфейсом, і вибір робиться під час виконання. У тестовому фреймворку типові приклади: різні способи логіну (UI, API, SSO), різні платіжні методи, генерація даних для різних регіонів.
interface LoginStrategy {
login(page: Page, user: User): Promise<void>;
}
class UiLogin implements LoginStrategy {
async login(page: Page, user: User) {
await page.goto('/login');
await page.getByLabel('Email').fill(user.email);
await page.getByLabel('Password').fill(user.password);
await page.getByRole('button', { name: 'Sign in' }).click();
}
}
class ApiLogin implements LoginStrategy {
async login(page: Page, user: User) {
const res = await page.request.post('/api/login', { data: user });
expect(res.ok()).toBeTruthy();
}
}
page.request ділить cookies з контекстом сторінки, тому після API-логіну сторінка вже автентифікована (якщо сесія тримається в cookies, а не в localStorage).
Вибір стратегії можна зробити через опцію фікстури, яку задає test.use або проєкт у конфігу. Тоді тест не знає, як саме відбувся логін.
Перевага перед if/else або switch: новий спосіб додається новим класом, без змін у тестах і в наявних стратегіях (Open/Closed).
На співбесіді: Strategy я використовую, коли є кілька взаємозамінних способів зробити одне й те саме, наприклад логін через UI або API. Тест працює з інтерфейсом, а конкретна реалізація обирається через опцію фікстури.
Опції фікстур як Strategy і коли патерн зайвий
У Playwright Test є готовий механізм для вибору поведінки: option-фікстури. Значення задається в projects або через test.use.
type Opts = { loginMode: 'ui' | 'api' };
export const test = base.extend<Opts & { loggedIn: void }>({
loginMode: ['api', { option: true }],
loggedIn: async ({ page, loginMode }, use) => {
const strategy = loginMode === 'ui' ? new UiLogin() : new ApiLogin();
await strategy.login(page, defaultUser);
await use();
},
});
test.describe('login flow', () => {
test.use({ loginMode: 'ui' });
// ...
});
Це поєднання Strategy і DI: тест залежить від loggedIn, а конкретна стратегія підставляється конфігурацією.
Коли Strategy НЕ потрібен:
- Варіантів лише два і нових не передбачається. Простий
if у фікстурі чесніший за інтерфейс і три класи. - Різниця між "стратегіями" в одному параметрі. Тоді досить передати параметр.
- Для різних браузерів і девайсів. Це вже робить сам Playwright через
projects і devices, писати свою стратегію браузера не треба.
Типова помилка на співбесіді: назвати Strategy будь-який switch. Strategy про поліморфізм: різні реалізації одного контракту, які можна підміняти незалежно.
На співбесіді: У Playwright я реалізую Strategy через option-фікстуру: значення приходить з projects або test.use, а фікстура обирає реалізацію. Якщо варіантів два і більше не буде, обходжуся простим if.