Introduction: Why Test Hooks Are Important in Playwright Automation
As Playwright automation projects grow, many test cases require the same setup and cleanup activities.
For example:
- Launching the application
- Logging in
- Creating test data
- Clearing cookies
- Closing database connections
If you write these steps inside every test, your framework becomes repetitive and difficult to maintain.
This is where Playwright Test Hooks become extremely useful.
Playwright Hooks allow you to execute code automatically before or after tests. They help reduce duplicate code, improve readability, and make your automation framework easier to manage.
Whether you are:
- QA Automation Engineer
- SDET
- Selenium Engineer transitioning to Playwright
- Software Testing Student
- Developer
- Interview Candidate
Understanding Playwright Test Hooks is an essential step toward building enterprise-level automation frameworks.
In this guide, you’ll learn:
- What Playwright Test Hooks are
- Hook execution order
- beforeAll()
- beforeEach()
- afterEach()
- afterAll()
- TypeScript examples
- Lifecycle diagrams
- Best practices
- Interview questions
- FAQs
Let’s begin.
What Are Playwright Test Hooks?
Playwright Test Hooks are special methods that automatically execute before or after test execution.
They are commonly used for:
- Test setup
- Test cleanup
- Login
- Database preparation
- Environment initialization
- Closing resources
Simple Definition
Playwright Test Hooks are lifecycle methods that automatically run before or after tests to prepare and clean up the testing environment.
Instead of repeating setup code inside every test, hooks execute it automatically.
Why Playwright Hooks Are Important
Imagine writing login steps inside every test.
Test 1
↓
Open Browser
↓
Login
↓
Execute Test
—————-
Test 2
↓
Open Browser
↓
Login Again
↓
Execute Test
Lots of duplicate code.
Using hooks:
beforeEach()
↓
Login
↓
Test Executes
↓
afterEach()
The setup code is written only once.
Benefits include:
- Cleaner test files
- Less duplicate code
- Better maintenance
- Easier framework design
- Consistent execution
Types of Playwright Hooks Explained
Playwright provides four main hooks.
| Hook | Runs |
| beforeAll() | Once before all tests |
| beforeEach() | Before every test |
| afterEach() | After every test |
| afterAll() | Once after all tests |
beforeAll() Explained
beforeAll() executes only once before any test starts.
Example uses:
- Database connection
- API authentication
- Test environment setup
- Shared resource initialization
Example:
import { test } from ‘@playwright/test’;
test.beforeAll(async () => {
console.log(“Application Setup”);
});
Output:
Application Setup
Test 1
Test 2
Test 3
Notice that setup happens only once.
beforeEach() Explained
This is the most frequently used hook.
It executes before every test.
Typical use cases:
- Login
- Open homepage
- Reset application state
- Create test data
Example:
test.beforeEach(async ({ page }) => {
await page.goto(‘https://example.com’);
});
Execution:
beforeEach
↓
Test 1
beforeEach
↓
Test 2
beforeEach
↓
Test 3
Every test begins from a consistent state.
afterEach() Explained
afterEach() runs immediately after every test.
Typical uses:
- Logout
- Delete temporary data
- Capture logs
- Take screenshots on failure
Example:
test.afterEach(async () => {
console.log(“Cleaning Test Data”);
});
afterAll() Explained
afterAll() executes once after all tests finish.
Example uses:
- Close database connection
- Delete temporary files
- Generate reports
- Release resources
Example:
test.afterAll(async () => {
console.log(“Framework Cleanup”);
});
Hook Execution Order
Understanding execution order is important.
Execution flow:
beforeAll()
↓
beforeEach()
↓
Test 1
↓
afterEach()
↓
beforeEach()
↓
Test 2
↓
afterEach()
↓
beforeEach()
↓
Test 3
↓
afterEach()
↓
afterAll()
This predictable lifecycle helps keep tests isolated and maintainable.
Real-World Playwright Test Hooks Example (TypeScript)
import { test, expect } from ‘@playwright/test’;
test.beforeAll(async () => {
console.log(“Starting Test Suite”);
});
test.beforeEach(async ({ page }) => {
await page.goto(‘https://example.com/login’);
});
test.afterEach(async () => {
console.log(“Cleaning Test Data”);
});
test.afterAll(async () => {
console.log(“Closing Test Suite”);
});
test(‘Successful Login’, async ({ page }) => {
await page.getByLabel(‘Username’).fill(‘admin’);
await page.getByLabel(‘Password’).fill(‘admin123’);
await page.getByRole(‘button’, {
name: ‘Login’
}).click();
await expect(page).toHaveURL(/dashboard/);
});
test(‘Forgot Password Page’, async ({ page }) => {
await page.getByText(‘Forgot Password’).click();
await expect(page).toHaveURL(/forgot-password/);
});
Output
Starting Test Suite
↓
beforeEach
↓
Login Test
↓
afterEach
↓
beforeEach
↓
Forgot Password Test
↓
afterEach
↓
Closing Test Suite
Test Hook Lifecycle Diagram
Playwright Test Runner
│
▼
beforeAll()
│
▼
beforeEach()
│
▼
Test Execution
│
▼
afterEach()
│
▼
Repeat for Next Test
│
▼
afterAll()
This lifecycle ensures that setup and cleanup happen consistently throughout the test suite.
Hooks vs Fixtures
One of the most common interview questions is:
“Should I use Hooks or Fixtures in Playwright?”
Although both help prepare the test environment, they serve different purposes.
Hooks
Hooks execute code before or after tests.
Examples:
- Open application
- Clean database
- Logout
- Generate logs
Fixtures
Fixtures provide reusable objects and resources to tests.
Examples:
- Logged-in page
- API client
- Browser Context
- Test data
Hooks vs Fixtures Comparison
| Feature | Playwright Hooks | Playwright Fixtures |
| Purpose | Setup and cleanup | Provide reusable resources |
| Execution | Before/after tests | Created when required |
| Reusability | Medium | Very High |
| Enterprise Usage | Common | Highly Recommended |
| Best For | Login setup, cleanup | Browser, API clients, authenticated pages |
When Should You Use Hooks?
Use Hooks when you need to:
- Prepare the environment
- Clean up after execution
- Create temporary data
- Reset application state
When Should You Use Fixtures?
Use Fixtures when you need reusable objects like:
- Authenticated page
- Browser Context
- API client
- Database connection
- Test users
Enterprise frameworks typically combine both Hooks and Fixtures.
Playwright Hooks vs TestNG and JUnit
Many Selenium engineers transition from TestNG or JUnit to Playwright. The lifecycle concepts are similar.
| Playwright | TestNG | JUnit |
| beforeAll() | @BeforeSuite / @BeforeClass | @BeforeAll |
| beforeEach() | @BeforeMethod | @BeforeEach |
| afterEach() | @AfterMethod | @AfterEach |
| afterAll() | @AfterSuite / @AfterClass | @AfterAll |
Key Difference
Playwright also provides powerful Fixtures, which are a core part of its test runner and are often preferred over large setup methods.
Enterprise Framework Hook Implementation
A scalable Playwright framework might look like this:
playwright-framework/
│
├── tests/
├── pages/
├── fixtures/
├── hooks/
│ login.ts
│ cleanup.ts
├── utils/
├── data/
├── playwright.config.ts
└── package.json
Example:
- beforeAll() → Initialize test environment.
- beforeEach() → Navigate to the application and prepare test data.
- afterEach() → Capture screenshots on failure and clean temporary data.
- afterAll() → Close connections and generate reports.
This approach keeps setup logic consistent across the framework.
Best Practices for Using Hooks
1. Keep Hooks Small
Each hook should perform one clear responsibility.
Good examples:
- Login
- Open application
- Create test data
Avoid combining many unrelated tasks in one hook.
2. Use beforeEach() for Test Isolation
Each test should begin with a predictable state.
Example:
test.beforeEach(async ({ page }) => {
await page.goto(‘/’);
});
3. Avoid Heavy Logic in beforeAll()
Reserve beforeAll() for shared initialization only.
Examples:
- Database connection
- Global authentication
- Environment preparation
4. Pair Hooks with Fixtures
Hooks prepare the environment, while Fixtures provide reusable objects.
Together they produce cleaner, enterprise-ready frameworks.
5. Keep Assertions Out of Hooks
Hooks should not validate business functionality.
Instead:
await expect(page).toHaveURL(…);
belongs inside test cases.
Common Mistakes to Avoid
Mistake 1: Logging In Inside Every Test
Instead of:
test(‘Login’, async ({ page }) => {
// login
});
Use:
test.beforeEach(async ({ page }) => {
// login
});
or an authentication Fixture.
Mistake 2: Large beforeEach() Methods
Avoid performing many unrelated tasks inside one hook.
Mistake 3: Ignoring Cleanup
If setup creates temporary resources, clean them in afterEach() or afterAll().
Mistake 4: Using Hooks Instead of Fixtures
Reusable resources should generally be implemented as Fixtures rather than large Hooks.
Mistake 5: Shared State Between Tests
Each test should remain independent to support reliable parallel execution.
Troubleshooting Guide
Hook Not Executing
Ensure the hook is declared before the relevant tests in the file.
Login Runs Multiple Times
Remember that beforeEach() executes before every test. If shared setup is sufficient, consider beforeAll() or a worker-scoped Fixture.
Cleanup Not Running
Check whether errors during setup are preventing later lifecycle methods from executing, and ensure resources are handled appropriately.
Slow Test Execution
Review expensive operations inside beforeEach(). Some initialization may be better suited to beforeAll() or Fixtures.
Playwright Test Hooks Interview Questions
1. What are Playwright Test Hooks?
Answer:
Lifecycle methods that execute automatically before or after tests.
2. What are the four Playwright Hooks?
Answer:
- beforeAll()
- beforeEach()
- afterEach()
- afterAll()
3. What is beforeEach() used for?
Answer:
Preparing every test with a consistent starting state, such as opening the application or logging in.
4. What is the difference between beforeAll() and beforeEach()?
Answer:
beforeAll() runs once before all tests, while beforeEach() runs before every individual test.
5. When should you use afterEach()?
Answer:
For cleanup tasks like deleting temporary data, logging information, or capturing screenshots after each test.
6. When should you use Fixtures instead of Hooks?
Answer:
Use Fixtures when providing reusable resources such as authenticated pages, API clients, or Browser Contexts.
7. Can Hooks improve code reuse?
Answer:
Yes. They centralize repeated setup and cleanup logic.
8. Do Hooks support parallel execution?
Answer:
Yes. They work within Playwright’s execution model, but shared state should be avoided.
9. Can multiple Hooks exist in one file?
Answer:
Yes. Multiple Hooks can be defined and will execute in declaration order where applicable.
10. Are Hooks mandatory?
Answer:
No. They are optional but highly recommended for reducing duplication and improving maintainability.
Frequently Asked Questions (FAQs)
What are Playwright Test Hooks?
Playwright Test Hooks are lifecycle methods that automatically execute before or after tests to manage setup and cleanup.
Are Hooks suitable for beginners?
Yes. They are simple to learn and help organize automation projects from the beginning.
Should I use Hooks or Fixtures?
Use Hooks for setup and cleanup tasks. Use Fixtures to provide reusable resources and objects to tests.
Can I use multiple Hooks together?
Yes. A test file can include beforeAll(), beforeEach(), afterEach(), and afterAll() together.
Do Hooks improve test maintenance?
Yes. By centralizing repeated logic, Hooks reduce duplication and make frameworks easier to maintain.
Can Hooks work with the Page Object Model?
Yes. A common enterprise approach is to use Hooks for setup, Page Objects for UI interactions, and Fixtures for reusable resources.
