Introduction: Why Every Playwright Engineer Should Understand the Playwright Test Lifecycle
If you are learning Playwright Automation Testing, one of the most important topics you should understand is the Playwright Test Lifecycle.
Many beginners learn how to write Playwright scripts but don’t know what happens internally before and after a test runs. Understanding the lifecycle helps you organize your tests, manage resources efficiently, and build scalable automation frameworks.
Whether you are:
- A QA Automation Engineer
- An SDET
- A Selenium engineer transitioning to Playwright
- A Software Testing Student
- A Developer learning Playwright
- Preparing for Playwright interviews
Learning the Playwright Test Lifecycle will help you write cleaner, reusable, and more maintainable automation code.
In this guide, you’ll learn:
- What the Playwright Test Lifecycle is
- How Playwright executes tests
- Hooks (beforeAll, beforeEach, afterEach, afterAll)
- Browser, BrowserContext, and Page lifecycle
- Fixtures and execution flow
- Enterprise framework implementation
- Best practices
- Interview questions
- FAQs
Let’s begin with the fundamentals.
What Is the Playwright Test Lifecycle?
The Playwright Test Lifecycle is the sequence of events that Playwright follows while executing automation tests.
It starts when the Playwright Test Runner begins execution and ends after all resources are cleaned up.
Simple Definition
The Playwright Test Lifecycle is the complete execution flow of a Playwright test, including setup, hooks, fixtures, test execution, cleanup, and reporting.
Understanding this lifecycle helps you:
- Organize test execution
- Avoid duplicate code
- Manage browsers efficiently
- Reduce flaky tests
- Build enterprise-ready automation frameworks
Why Is the Playwright Test Lifecycle Important?
Understanding the lifecycle provides several benefits:
- Cleaner test organization
- Better resource management
- Faster execution
- Improved test isolation
- Easier debugging
- Reusable setup and teardown logic
Most automation interviewers ask lifecycle-related questions because they indicate your understanding of Playwright’s architecture.
Playwright Test Execution Flow
The Playwright Test Runner follows a well-defined execution order.
Playwright Test Runner
│
▼
beforeAll()
│
▼
beforeEach()
│
▼
Test Execution
│
▼
afterEach()
│
▼
afterAll()
│
▼
HTML Report
Every Playwright test follows this lifecycle.
How the Playwright Test Lifecycle Works
Let’s understand each stage.
1. Test Runner Starts
The Playwright Test Runner begins execution.
It:
- Reads configuration
- Discovers test files
- Creates workers
- Loads fixtures
2. beforeAll()
Runs once before any test in the file.
Common uses:
- Launch database
- Initialize API clients
- Create test data
- Read configuration
3. beforeEach()
Runs before every individual test.
Common uses:
- Login
- Open application
- Create Browser Context
- Prepare test data
4. Test Execution
The actual automation steps run here.
Example:
- Login
- Search
- Add product
- Checkout
5. afterEach()
Runs after every test.
Common uses:
- Capture screenshots
- Close Browser Context
- Delete test data
- Clear temporary files
6. afterAll()
Runs once after all tests finish.
Common uses:
- Close browser
- Disconnect database
- Generate reports
- Send notifications
Lifecycle Diagram (Text-Based)
Playwright Test Runner
│
▼
beforeAll()
│
▼
beforeEach()
│
▼
Browser Launch
│
▼
Browser Context
│
▼
Page
│
▼
Test Steps
│
▼
Assertions
│
▼
afterEach()
│
▼
afterAll()
│
▼
HTML Report
Browser, BrowserContext, and Page Lifecycle
Playwright manages browser resources efficiently.
Browser
│
▼
Browser Context
│
▼
Page
│
▼
Website
Browser
Represents the browser instance.
Example:
const browser = await chromium.launch();
Browser Context
Represents an isolated session.
Example:
const context = await browser.newContext();
Page
Represents a browser tab.
Example:
const page = await context.newPage();
Each component is created and destroyed according to the lifecycle, ensuring test isolation.
Real-World Example
Imagine testing an e-commerce website.
The lifecycle is:
- Launch browser
- Create Browser Context
- Open page
- Login
- Search product
- Add to cart
- Checkout
- Verify confirmation
- Close context
- Close browser
Following this sequence keeps tests isolated and repeatable.
Understanding Hooks in the Playwright Test Lifecycle
Hooks are one of the most important concepts in the Playwright Test Lifecycle. They allow you to execute setup and cleanup code automatically before or after tests.
Instead of repeating the same code in every test, you can place it inside hooks.
For example, imagine you have 100 login test cases.
Without hooks, you would have to open the browser and navigate to the application 100 times by writing the same code repeatedly.
Hooks solve this problem.
What Are Playwright Hooks?
Playwright provides four lifecycle hooks:
- beforeAll()
- beforeEach()
- afterEach()
- afterAll()
Each hook runs at a different stage of the Playwright Test Lifecycle.
Playwright Test Runner
│
▼
beforeAll()
│
▼
beforeEach()
│
▼
Test Execution
│
▼
afterEach()
│
▼
afterAll()
Let’s understand each hook with practical examples.
beforeAll()
The beforeAll() hook runs only once before all tests in a test file.
It is commonly used for:
- Launching a browser
- Reading configuration files
- Connecting to a database
- Creating test data
- Setting up APIs
Example
import { test } from ‘@playwright/test’;
test.beforeAll(async () => {
console.log(“Opening Application Resources”);
});
test(“Test 1”, async () => {
console.log(“Running Test 1”);
});
test(“Test 2”, async () => {
console.log(“Running Test 2”);
});
Output
Opening Application Resources
Running Test 1
Running Test 2
Notice that beforeAll() executes only once.
beforeEach()
The beforeEach() hook runs before every test case.
It is the most commonly used Playwright hook.
Typical uses include:
- Login
- Opening the application
- Creating Browser Context
- Creating Page objects
- Preparing test data
Example
import { test } from ‘@playwright/test’;
test.beforeEach(async ({ page }) => {
await page.goto(“https://example.com”);
});
test(“Home Page”, async ({ page }) => {
console.log(“Executing Home Test”);
});
test(“About Page”, async ({ page }) => {
console.log(“Executing About Test”);
});
Execution Flow
beforeEach()
↓
Home Page Test
↓
beforeEach()
↓
About Page Test
Each test starts with a fresh setup.
afterEach()
The afterEach() hook executes after every test.
It is commonly used for:
- Capturing screenshots
- Closing Browser Context
- Logging results
- Cleaning test data
- Removing temporary files
Example
import { test } from ‘@playwright/test’;
test.afterEach(async () => {
console.log(“Cleaning Test Data”);
});
test(“Login Test”, async () => {
console.log(“Executing Login”);
});
Output
Executing Login
Cleaning Test Data
afterAll()
The afterAll() hook runs once after every test has completed.
Typical uses:
- Close browser
- Disconnect database
- Generate reports
- Send email notifications
Example
import { test } from ‘@playwright/test’;
test.afterAll(async () => {
console.log(“Closing Browser”);
});
test(“Test One”, async () => {
});
test(“Test Two”, async () => {
});
Output
Test One
Test Two
Closing Browser
Complete Hook Execution Order
Suppose we have two test cases.
import { test } from ‘@playwright/test’;
test.beforeAll(async () => {
console.log(“beforeAll”);
});
test.beforeEach(async () => {
console.log(“beforeEach”);
});
test.afterEach(async () => {
console.log(“afterEach”);
});
test.afterAll(async () => {
console.log(“afterAll”);
});
test(“Test 1”, async () => {
console.log(“Executing Test 1”);
});
test(“Test 2”, async () => {
console.log(“Executing Test 2”);
});
Output
beforeAll
beforeEach
Executing Test 1
afterEach
beforeEach
Executing Test 2
afterEach
afterAll
This sequence represents the standard Playwright Test Lifecycle.
Test Fixtures and Lifecycle
Fixtures provide reusable resources for your tests. Playwright automatically creates and cleans them up based on their scope.
Common built-in fixtures include:
- browser
- context
- page
- request
Example:
import { test, expect } from ‘@playwright/test’;
test(“Login Test”, async ({ page }) => {
await page.goto(“https://example.com”);
await expect(page).toHaveTitle(/Example/);
});
Here, the page fixture is created before the test begins and cleaned up automatically afterward.
Fixture Scope
Playwright supports different fixture scopes.
| Scope | Description |
| Test Scope | A new fixture instance is created for every test. |
| Worker Scope | One fixture instance is shared across tests running in the same worker process. |
Test Scope
Use when each test should have a completely isolated environment.
Example:
Test 1
↓
New BrowserContext
↓
New Page
—————-
Test 2
↓
New BrowserContext
↓
New Page
Worker Scope
Use for expensive resources that can safely be shared within a worker.
Examples:
- Database connections
- API clients
- Configuration
- Shared authentication setup
Real-World Enterprise Lifecycle
Most enterprise Playwright frameworks follow this flow:
Playwright Test Runner
│
▼
Load Configuration
│
▼
beforeAll()
│
▼
Create Browser
│
▼
beforeEach()
│
▼
Create BrowserContext
│
▼
Create Page
│
▼
Execute Test
│
▼
Capture Screenshots (if needed)
│
▼
afterEach()
│
▼
Close BrowserContext
│
▼
afterAll()
│
▼
Close Browser
│
▼
Generate HTML Report
This approach keeps tests isolated, reusable, and easy to maintain.
Why Hooks Matter in Enterprise Projects
Hooks help automation teams:
- Reduce duplicate code
- Improve framework maintainability
- Manage browser resources efficiently
- Ensure consistent test setup and cleanup
- Build scalable automation frameworks for large projects
Best Practices for Managing the Playwright Test Lifecycle
Managing the Playwright Test Lifecycle correctly is one of the biggest differences between a beginner and an experienced automation engineer. A well-designed lifecycle improves execution speed, reduces flaky tests, and makes automation frameworks easier to maintain.
Follow these best practices to build scalable Playwright frameworks.
1. Use Hooks Only for Common Setup and Cleanup
Hooks should contain only reusable setup and cleanup code.
Good Examples
- Launch Browser
- Create Browser Context
- Open Application
- Login
- Capture Screenshot
- Close Browser Context
Avoid
- Business validations
- Assertions
- Test-specific logic
Keep test-specific actions inside the actual test case.
2. Create a Fresh Browser Context for Every Test
Playwright is designed around isolated Browser Contexts.
Example:
test.beforeEach(async ({ browser }) => {
const context = await browser.newContext();
});
Benefits
- Independent sessions
- Fresh cookies
- Fresh Local Storage
- No shared login
- Reliable execution
3. Close Browser Context After Every Test
Always clean resources.
Example
test.afterEach(async ({ context }) => {
await context.close();
});
This:
- Releases memory
- Removes temporary data
- Improves execution speed
4. Keep beforeAll() Lightweight
Good use cases:
- Read Configuration
- Load Test Data
- Connect Database
- Create API Client
Avoid launching Pages or Browser Contexts here unless they are intentionally shared.
5. Prefer Fixtures Over Global Variables
Bad Practice
let page;
Good Practice
test(‘Login’, async ({ page }) => {
});
Fixtures improve readability and prevent shared state issues.
6. Never Use Hard Waits
Avoid
await page.waitForTimeout(5000);
Instead
await page.getByRole(‘button’, {
name: ‘Login’
}).click();
Playwright automatically waits for elements to become actionable.
7. Use Page Object Model (POM)
Separate page logic from test logic.
Example Structure
tests/
pages/
fixtures/
utils/
config/
reports/
This makes your framework modular and maintainable.
8. Keep Tests Independent
Every test should:
- Create its own data
- Use its own Browser Context
- Run without depending on previous tests
Independent tests are easier to debug and execute in parallel.
9. Capture Screenshots on Failure
Example
test.afterEach(async ({ page }, testInfo) => {
if (testInfo.status !== testInfo.expectedStatus) {
await page.screenshot({
path: ‘failure.png’
});
}
});
Screenshots help identify UI issues quickly.
10. Use HTML Reports
Enable Playwright’s built-in HTML Reporter to review execution results, screenshots, traces, and failure details.
Common Mistakes to Avoid
Many automation engineers make lifecycle-related mistakes that lead to unstable frameworks.
Mistake 1: Repeating Login in Every Test
Instead of writing:
await page.goto(…);
await page.fill(…);
await page.click(…);
inside every test, move common setup into beforeEach() or reusable fixtures.
Mistake 2: Using One Browser Context Everywhere
Sharing one Browser Context causes:
- Shared cookies
- Shared authentication
- Flaky tests
Create a new Browser Context for independent tests.
Mistake 3: Forgetting Cleanup
Always close:
- Browser Context
- Browser
- Database Connections
- Temporary Files
Mistake 4: Putting Assertions Inside Hooks
Avoid
test.beforeEach(async () => {
expect(…);
});
Assertions belong inside test cases.
Mistake 5: Large beforeEach() Methods
Avoid performing unnecessary setup.
Keep hooks focused and efficient.
Playwright Test Lifecycle vs Selenium TestNG Lifecycle
Many Selenium engineers transitioning to Playwright ask how the lifecycle compares.
| Feature | Playwright | Selenium + TestNG |
| Test Runner | Built-in | External (TestNG/JUnit) |
| Hooks | beforeAll, beforeEach, afterEach, afterAll | @BeforeSuite, @BeforeClass, @BeforeMethod, @AfterMethod, etc. |
| Browser Management | Built-in | Manual |
| Browser Context | Built-in | Not available |
| Auto Waiting | Yes | Manual waits required |
| Parallel Execution | Built-in | Configuration required |
| HTML Reports | Built-in | Third-party tools often used |
| Fixtures | Built-in | Custom implementation |
Enterprise Test Lifecycle Workflow
Large organizations follow a structured execution flow.
Developer Commit
│
▼
GitHub / Azure DevOps
│
▼
Playwright Test Runner
│
▼
Load Configuration
│
▼
beforeAll()
│
▼
beforeEach()
│
▼
Create BrowserContext
│
▼
Create Page
│
▼
Execute Test
│
▼
Assertions
│
▼
Capture Screenshot (On Failure)
│
▼
afterEach()
│
▼
Close BrowserContext
│
▼
afterAll()
│
▼
Generate HTML Report
│
▼
CI/CD Pipeline Result
This lifecycle ensures consistent execution across local environments and CI/CD pipelines.
Playwright Test Lifecycle Interview Questions
1. What is the Playwright Test Lifecycle?
Answer:
The Playwright Test Lifecycle is the sequence of events that occurs during test execution, including setup, hooks, fixtures, test execution, cleanup, and reporting.
2. Which hooks are available in Playwright?
Answer:
- beforeAll()
- beforeEach()
- afterEach()
- afterAll()
3. What is the purpose of beforeEach()?
Answer:
It runs before every test and is commonly used to navigate to the application, log in, or prepare test data.
4. What is the purpose of afterEach()?
Answer:
It performs cleanup after each test, such as closing Browser Contexts, capturing screenshots, or deleting temporary data.
5. What are Playwright fixtures?
Answer:
Fixtures are reusable resources provided by Playwright, such as browser, context, page, and request, which simplify setup and cleanup.
6. What is the difference between Test Scope and Worker Scope?
Answer:
- Test Scope: Creates a new fixture instance for each test.
- Worker Scope: Shares a fixture instance across tests running in the same worker process.
7. Why should Browser Contexts be closed after tests?
Answer:
Closing Browser Contexts releases memory, removes session data, and keeps tests isolated.
8. Why is the Playwright Test Lifecycle important?
Answer:
It helps organize setup and cleanup, improves maintainability, enables resource management, and supports scalable automation frameworks.
9. How does Playwright improve test reliability?
Answer:
Playwright provides Auto Waiting, isolated Browser Contexts, built-in fixtures, and predictable lifecycle management.
10. How does Playwright differ from Selenium in lifecycle management?
Answer:
Playwright includes a built-in test runner, hooks, fixtures, and browser management, whereas Selenium typically relies on external frameworks like TestNG or JUnit for lifecycle management.
Frequently Asked Questions (FAQs)
What is the Playwright Test Lifecycle?
It is the complete sequence of setup, execution, cleanup, and reporting performed by Playwright while running tests.
Why are hooks important?
Hooks reduce duplicate code by handling common setup and teardown tasks automatically.
When should I use beforeAll()?
Use it for tasks that should run once, such as loading configuration or creating shared resources.
When should I use beforeEach()?
Use it for tasks that every test requires, such as opening the application or creating a fresh Browser Context.
Can I use multiple hooks in one file?
Yes. You can use all four lifecycle hooks together in the same test file.
What are fixtures in Playwright?
Fixtures are reusable objects that automatically manage resources like browsers, pages, contexts, and API clients.
Should every test create a new Browser Context?
For most UI tests, yes. This ensures proper isolation and prevents session leakage between tests.
Does Playwright automatically clean up resources?
Yes. Built-in fixtures are cleaned up automatically, but custom resources should also be closed explicitly when needed.
