- Published on
Playwright - Lazy Gettery Page Object Modelu
Playwright - Inicializace lokátorů v Page Object Modelu
Při psaní testů v Playwrightu je běžné používat Page Object Model (POM), což je návrhový vzor pro organizaci testovací logiky a lokátorů v samostatných třídách. Je vhodné při použití POM v Playwrightu inicializovat všechny lokátory přímo v konstruktoru třídy? V dokumentaci Playwrightu je uveden příklad použití POM, kde inicializují všechny lokátory v konstruktoru - Playwright POM dokumentace. Jaké má tento přístup výhody a nevýhody? A jaká existuje alternativa?
Pozn.: V Playwrightu jsou lokátory vždy “lazy” – ať je vytvoříme v konstruktoru, nebo přes getter. Vytvoření lokátoru neprovádí žádné vyhledávání v DOM; k reálnému hledání dojde až při použití (klik, čtení textu, expect(...)).
Výhody inicializace všech lokátorů v konstruktoru
- přehlednost: hned vidím, s kterými lokátory může page object pracovat
- centralizace: všechny lokátory na jednom místě
Nevýhody
- zbytečná inicializace (alokace paměti) v případě, že test daný lokátor nepoužije
- problémy u dynamických prvků – pokud stránka obnoví obsah (např. iframe, reload sekce stránky), lokátor uložený v konstruktoru může ukazovat na starý kontext
Tyto nevýhody můžeme odstranit, když lokátory nebudeme inicializovat v konstruktoru, ale použijeme lazy gettery. Lokátory se tak inicializují pouze tehdy, pokud je skutečně použijeme.
Inicializace lokátorů v konstruktoru
import { Locator, Page, expect } from '@playwright/test'
export class ListOfUsers {
private readonly idItems: Locator
private readonly nameItems: Locator
private readonly emailItems: Locator
private readonly userItems: Locator
constructor(private page: Page) {
this.idItems = page.getByTestId('id-item')
this.nameItems = page.getByTestId('name-item')
this.emailItems = page.getByTestId('email-item')
this.userItems = page.getByTestId('user-item')
}
public async expectUserCount(count: number): Promise<void> {
await expect(this.userItems).toHaveCount(count)
}
}
Použití lazy getterů místo konstruktoru
import { Page, expect } from '@playwright/test'
export class ListOfUsers {
constructor(private page: Page) {}
get idItems() {
return this.page.getByTestId('id-item')
}
get nameItems() {
return this.page.getByTestId('name-item')
}
get emailItems() {
return this.page.getByTestId('email-item')
}
get userItems() {
return this.page.getByTestId('user-item')
}
public async expectUserCount(count: number): Promise<void> {
await expect(this.userItems).toHaveCount(count)
}
}
Použití lazy getterů v testu
Představme si jednoduchý test, který ověřuje počet zobrazených uživatelů na stránce. Pomocí instance třídy ListOfUsers můžeme přistupovat k metodě expectUserCount, která interně využívá getter userItems. Díky tomu se lokátor user-item vytvoří až ve chvíli, kdy ho opravdu potřebujeme – v tomto případě během provádění assertu (expect).
import { test, expect } from '@playwright/test'
import { ListOfUsers } from '../page-objects/ListOfUsers'
test('should display the correct number of users', async ({ page }) => {
await page.goto('https://example.com/users')
const listOfUsers = new ListOfUsers(page)
await listOfUsers.expectUserCount(5)
})
Getter userItems v pozadí zavolá this.page.getByTestId('user-item'), ale až tehdy, když k němu v metodě expectUserCount přistoupíme. Tím se vyhneme zbytečné inicializaci lokátorů, které test vůbec nevyužije. Výsledkem je přehlednější konstruktor a efektivnější práce s pamětí.
Shrnutí
Inicializace lokátorů v konstruktoru nabízí přehlednost a centralizaci, ale může vést ke zbytečné inicializaci nevyužitých prvků. V Playwrightu jsou Locator objekty vždy lazy – samotné vytvoření neprohledává DOM. Rozdíl mezi konstruktorem a getterem je v okamžiku vytvoření tohoto objektu:
- Konstruktor – lokátor je vytvořen při instanciaci POM. Hodí se pro stabilní stránky, kde se struktura nemění a chceme mít všechny lokátory hned pohromadě.
- Getter – lokátor se vytvoří až při použití. To je užitečné u dynamických scénářů (reloady, měnící se iframy) a udržuje konstruktor čistý. V praxi můžeš oba přístupy kombinovat – statické prvky v konstruktoru, dynamické přes gettery.

