- Published on
Playwright - proč se v testu změní hodnota v konstantě?
Problém: proč mám pokaždé jiný e-mail?
Při psaní API testů si často potřebujeme vygenerovat unikátní hodnotu nějaké proměnné - např. e-mail pro uživatele.
V prvním testu ho chceme úspěšně použít jako přihlašovací jméno pro registraci.
V druhém testu pak stejný e-mail použijeme znovu, abychom ověřili, že duplicitní registrace selže. Na začátku, ještě před definicí testů, si tedy vygenerujeme jedinečný e-mail:
const validUser = {
email: `testuser_${Date.now()}@example.com`,
password: 'MyPassword',
}
test.describe('Authentication API', () => {
// ...
test('1.1 Should register successfully with valid email and password', async () => {
console.log('Test 1.1 user: ' + validUser)
// ...
})
test.only('1.2 Should fail registration with an already existing email', async () => {
console.log('Test 1.2 user: ' + validUser)
// ...
})
})
Na první pohled to vypadá v pořádku - vytvoříme unikátní e-mail, který pak použijeme v každém jednotlivém testu.
Jenže při spuštění sady testů se ukáže, že v každém testu se použil jiný e-mail. Výsledek:
Test 1.1 user: testuser_1753241517705@example.com
Test 1.2 user: testuser_1753241517730@example.com
Druhý test pak není skutečně duplicitní registrace – ve skutečnosti testujete úplně jiného uživatele a registrace naproti očekávání proběhne úspěšně.
Proč se to děje?
Playwright spouští každý test(...) izolovaně. Soubor s testy se znovu načte a proměnné na top-levelu se znovu vyhodnotí.
Jinými slovy, tento kód:
const validUser = {
email: `testuser_${Date.now()}@example.com`,
password: 'MyPassword',
}
neběží jen jednou, ale při každém testu znovu. Proto máme pokaždé jiný e-mail.
Řešení:
Horší varianta: použít beforeAll
Chceme, aby se validUser vygeneroval jen jednou před celou sadou testů - to zajistí test.beforeAll.
Správné řešení:
import { test, expect, request } from '@playwright/test'
const BASE_URL = 'http://localhost:3000'
const REGISTER_ENDPOINT = `${BASE_URL}/register`
let validUser
test.describe('Authentication API', () => {
let apiContext
test.beforeAll(async ({ playwright }) => {
apiContext = await request.newContext()
// vygenerujeme JEDNOU pro celou sadu testů
validUser = {
email: `testuser_${Date.now()}@example.com`,
password: 'MyPassword',
}
console.log('unique test user:', validUser)
})
test.afterAll(async () => {
await apiContext.dispose()
})
test('1.1 registrace nového uživatele', async () => {
console.log('Test 1.1 user:', validUser)
const res = await apiContext.post(REGISTER_ENDPOINT, { data: validUser })
expect(res.status()).toBe(201)
})
test('1.2 registrace duplicitního uživatele', async () => {
console.log('Test 1.2 user:', validUser)
const res = await apiContext.post(REGISTER_ENDPOINT, { data: validUser })
expect(res.status()).toBe(400)
})
})
Toto řešení samo o sobě může opět skončit neúspěchem kvůli paralelnímu spouštění testů. Je třeba testy pustit sekvenčně:
npx playwright test --workers=1
Lepší varianta: psát testy, které jsou na sobě nezávislé
Psát testy závislé na jiných testech je obecně antipattern.
Důvody:
Izolovanost testů Každý test by měl být spustitelný samostatně, bez ohledu na pořadí. Pokud je test závislý na výsledku jiného, ztrácíme možnost spouštět je selektivně (test.only, --grep, paralelně apod.).
Paralelní běh Playwright (i jiné frameworky) běžně spouští testy paralelně. Pokud mají závislosti, mohou selhat jen proto, že běžely ve špatném pořadí nebo ve více vláknech současně.
Snadnější ladění a údržba Když test selže, má být jasné, že problém je buď v testu samotném, nebo v aplikaci. Pokud závisí na jiném testu, může selhat i kvůli němu.
Shrnutí
- Proměnné na top-levelu v Playwright testech se vyhodnocují pro každý test zvlášť.
- Pokud chcete sdílená data pro celou
describeskupinu, použijtebeforeAll. - Jednoduše lze problému předejít psaním na sobě nezávislých testů

