48 / 80 · 12 Programming for QA · Async/Await Patterns← prev⊞ allnext →☰ Read as one page
6.5Common Async Pitfalls
1. Forgotten Await
Already covered above — the most dangerous bug because the test passes silently.
Detection: Use TypeScript strict mode and ESLint rules like @typescript-eslint/no-floating-promises.
2. Race Conditions in Tests
// BUG: click may happen before navigation completes
await page.goto('/products');
page.click('[data-testid="first-product"]'); // missing await!
await expect(page).toHaveURL(/\/products\/\d+/); // may fail intermittently
// FIX: await the click
await page.goto('/products');
await page.click('[data-testid="first-product"]');
await expect(page).toHaveURL(/\/products\/\d+/);
3. Shared State Between Concurrent Tests
# BUG: parallel tests modify the same user
async def test_update_name():
await api.patch("/users/1", json={"name": "Alice"})
user = await api.get("/users/1")
assert user["name"] == "Alice" # May fail if test_update_email runs concurrently
async def test_update_email():
await api.patch("/users/1", json={"email": "new@test.com"})
user = await api.get("/users/1")
assert user["email"] == "new@test.com"
# FIX: each test creates its own user
async def test_update_name():
user = await api.post("/users", json={"name": "Original"})
await api.patch(f"/users/{user['id']}", json={"name": "Alice"})
updated = await api.get(f"/users/{user['id']}")
assert updated["name"] == "Alice"
4. Unhandled Promise Rejections
// BUG: if the fetch fails, the error is swallowed
fetch('/api/cleanup').then(r => r.json()); // no await, no catch
// FIX: always await or catch
try {
await fetch('/api/cleanup');
} catch (e) {
console.warn('Cleanup failed:', e);
}
5. Event Loop Blocking
# BUG: synchronous sleep blocks the event loop
async def test_with_delay():
import time
time.sleep(5) # Blocks the entire event loop for 5 seconds!
# FIX: use async sleep
async def test_with_delay():
await asyncio.sleep(5) # Non-blocking