๐ฅ Hooks, Fixtures, and Grouping in Playwright - Explained with Examples
1. What is a Test and Test Case in Playwright?
✅ Test (test)
- A single unit of testing logic.
- Defined using
test()function. - Each
test()represents a test case.
import { test, expect } = require('@playwright/test');
test('Verify title of homepage', async ({ page }) => {
await page.goto('https://example.com');
await expect(page).toHaveTitle(/Example Domain/);
});
In this example, "Verify title of homepage" is the test case description.
๐น 2. What are before, after, beforeEach, afterEach?
These are hooks to manage setup and teardown logic:
- ๐ beforeAll: Runs once before all tests
- ๐ afterAll: Runs once after all tests
- ๐ beforeEach: Runs before each test case
- ๐ afterEach: Runs after each test case
๐น 3. Step-by-Step Example
const { test, expect } = require('@playwright/test');
test.beforeAll(async () => {
console.log('>> BEFORE ALL: Setting up resources');
});
test.afterAll(async () => {
console.log('>> AFTER ALL: Cleaning up resources');
});
test.beforeEach(async ({ page }) => {
console.log('>> BEFORE EACH: Navigating to homepage');
await page.goto('https://example.com');
});
test.afterEach(async () => {
console.log('>> AFTER EACH: Test completed');
});
test('Test Case 1 - Check title', async ({ page }) => {
await expect(page).toHaveTitle(/Example Domain/);
});
test('Test Case 2 - Check heading text', async ({ page }) => {
const heading = await page.locator('h1');
await expect(heading).toHaveText('Example Domain');
});
๐น 4. Execution Flow
beforeAll()runs once- Each test follows:
beforeEach()→ test body →afterEach() afterAll()runs once after all tests
๐น 5. Why Use Hooks?
- DRY code: Avoid repetition
- Maintainability: Centralize test environment handling
- Consistency: Predictable state across tests
๐ Expanded Understanding with More Points
๐ธ 6. Grouping Tests using test.describe()
Group related test cases with shared setup logic:
const { test, expect } = require('@playwright/test');
test.describe('Homepage Tests', () => {
test.beforeEach(async ({ page }) => {
await page.goto('https://example.com');
});
test('has correct title', async ({ page }) => {
await expect(page).toHaveTitle(/Example Domain/);
});
test('has correct heading', async ({ page }) => {
const heading = await page.locator('h1');
await expect(heading).toHaveText('Example Domain');
});
});
๐ข Advantage: Better readability and shared setup.
๐ธ 10. Tagging/Filtering Tests
Control which tests run during development:
test.only(...): Runs only this testtest.skip(...): Skips this testtest.fixme(...): Marks the test as broken or to be fixed
test.skip('This feature is under development', async ({ page }) => {
// This test will be skipped
});
๐ธ 11. Fixtures for Reusable Setups
Create custom logic for shared test setup like login, data, etc.:
// In playwright.config.js or a setup file
const base = require('@playwright/test');
exports.test = base.test.extend({
adminPage: async ({ browser }, use) => {
const context = await browser.newContext({ storageState: 'auth.json' });
const page = await context.newPage();
await use(page);
await page.close();
},
});
Use the fixture in test:
const { test, expect } = require('./custom-fixtures');
test('Admin can access dashboard', async ({ adminPage }) => {
await adminPage.goto('/admin');
await expect(adminPage.locator('h1')).toHaveText('Admin Dashboard');
});
๐ธ 12. Use Annotations for Metadata
Attach custom metadata for better tracking:
test('Critical path - checkout flow', async ({ page }) => {
test.info().annotations.push({ type: 'severity', description: 'high' });
});
Summary Table
| Component | Purpose |
|---|---|
test() |
Defines a single test case |
beforeAll() / afterAll() |
Run once before/after all tests |
beforeEach() / afterEach() |
Run before/after each test |
test.describe() |
Group related test cases |
test.only / skip / fixme |
Control test execution |
| Fixtures | Reusable logic and objects |
Comments
Post a Comment