Playwright test hooks also allow you to individually map out test cases, initialise test variables and close test cases without any dependency for a successful front-end user experience.
This article dives deep into how these hooks keep your tests organised, reliable, and free from unexpected failures.
What are Test Hooks in Playwright?
Test hooks are special functions that automate the setup and teardown actions inside every test case.
So if you want to test your web user interface on a different platform or browser, you can simply declare test hooks and start executing tests instantly without worrying about external configuration.
Test hooks can work with local or globally declared test classes within test scripts to load tests easily across platforms, operating systems, browsers and devices to initiate tests faster and eliminate code duplication.
There are four kinds of test hooks, i.e., beforeAll, beforeEach, afterAll and afterEach, offered in the Playwright test runner to automate test runs efficiently. Test hooks run once and handle all setup and teardown tasks so that you can execute test cases without repeatedly worrying about code duplication or new configuration.
These hooks ensure that your tests run in a clean and a predictable environment, which is crucial for reliability and speed in automation testing.
How to Execute Test Hooks in Playwright: BeforeAll, BeforeEach, AfterAll and AfterEach
Test hooks like Playwright BeforeAll, BeforeEach, AfterEach and AfterAll run in individual test suites within the Playwright Test Runner, which is an automation framework for QA testers.
Each hook aids in smooth test experiences and structured test case creation to ensure maximum app compatibility.
While hooks like BeforeAll and AfterAll are executed at the very beginning of the testing phase, BeforeEach and AfterEach are run before individual element testing.
The scoping rules for these hooks are as follows: BeforeAll and AfterAll are scoped globally for the entire test suite, while BeforeEach and AfterEach are scoped to individual tests. This means BeforeEach and AfterEach will run multiple times, while BeforeAll and AfterAll only run once.]
By automating repetitive environment configuration (like setting path, launching browsers, injecting authentication states or cleaning test databases), hooks ensure overall QA testing is lean and fast.
Here is a deep dive on all the four major test hooks to understand where they come exactly in the whole UI testing workflow.
1. BeforeAll: Worker-level Global Initialization
Before you understand BeforeAll, you need to be aware of what a worker is. A worker is an independent operating system in Playwright to execute two or more test suites concurrently.
This means, if you have 2 test files, each test file containing multiple test suites and cases, you can execute them concurrently in isolated workers. Each worker has its own browser coverage, context and memory space.
The BeforeAll hook executes exactly once within a given worker process before any test cases in that file begin running.
It is typically used to perform setup tasks that only need to be done once, like test database seeding, launching browsers or setting global configurations.
BeforeAll eliminates the need to rewrite code for loading automated UI tests for a different browser or device, ensuring cross-browser compatibility or setting new test configurations for mobile devices.
This global functionality takes care of it all in the Playwright test runner so that you can run UI tests across multiple devices easily.
Code Example:
beforeAll(async () => { await browserContext.initialize();
});What Are The Core Use Cases of beforeAll in the Playwright Test Runner?
Since BeforeAll is executed once per worker, it takes care of all the browser-orientated configuration for the tester.
The playwright isolates test files by running them in separate workers; therefore, BeforeAll is the first function that triggers for a test file, ensuring smooth parallel execution.
Here are the core use cases of BeforeAll:
- Database initialisation: Initiating resource-heavy, recurrent global database creation for every test suite for seamless cross-device integration.
- File Configuration: Launching shared application servers, global setup and background microservices for each test file for seamless and stress-free test automation.
- Server computing: Setting up large, complex datasets or complex, unmutated server files (like HTTP porting, proxy connections or virtual machine access) is required for the entire test file.
- Enterprise e-commerce testing: Generating a massive product catalogue database entry once, allowing 50 individual test cases to test the UI components without rebuilding the catalogue every time.
- Financial ledger verification: Automating a core payment gateway API once at the worker level to retrieve a single high-privilege session token shared all across transactional tests.
While `beforeAll` helps provide global configuration support within the Playwright test runner, another hook described as `beforeEach ‘manages pre-approved checks for every test case.
2. BeforeEach: Context-Level Test Isolation
The beforeEach test hook is a single iteration that runs before every individual test case in the test file.
If you are testing for end-user experience in a browser, this hook will be triggered before any local class, i.e., authenticating login flow, screen loading, viewport, buttons or visual testing.
The BeforeEach hook executes prior to every single test case in the suite. It is the primary defence mechanism used to ensure that every part of the website is tested in isolation and eliminate the risk of flakiness caused by bleeding test cases.
Code Example:
beforeEach(async () => { await page.goto(‘https://example.com’);
});What Are The Core Use Cases of beforeEach in the Playwright Test Runner?
As BeforeEach is local to a test file, it will only allow you to run tests specific to what browsers are supported in that particular file runner. Let’s look at some use cases of BeforeEach:
- Browser Navigation: Navigating the browser page instance to a precise starting URL or application state.
- Database cleanup: Clearing browser cookies, local storage, and session data to wipe out traces of previous interactions.
- Element testing: Injecting specific UI element states, user roles or environment tokens directly into the browser context without any external path configuration.
- AI/LLM chat interface testing: beforeEach resets the customer chatbot canvas and clears the old conversation before every test so that customers are able to start a new conversation.
- SaaS multi-tenant testing: Authenticating the login for a unique persona (e.g., admin, editor, or viewer) immediately before their unique login test case fires.
The beforeEach hook helps you automate component testing and unit testing within independent test files to ensure you have a proper testing lifecycle without worrying about code leaks or memory leaks.
3. AfterEach: Test-Specific Environment Cleanup
The AfterEach hook runs immediately after the completion of every test case, confirming the status of the test and isolating the test environment for the next case.
Regardless of whether the test case has passed, failed, flaked or timed out, AfterEach is that final confirmation that the “test has been successfully completed” for other test cases to run.
For example, after the QA lead executes a test file for a dedicated browser, AfterEach clears browser history, closes the browser, resets variables and clears up test data. This hook ensures that test effects do not persist to the next, keeping the test environment consistent.
Code Example:
afterEach(async () => { await page.close();
});What Are The Core Use Cases of AfterEach in the Playwright Test Runner?
- Capture contextual telemetry: Give a detailed console report or compilation insights such as custom page console logs, network trace logs or application states the moment a test fails or freezes.
- Database cleanup: Delete allocated memory or application state changes created during that specific test execution’s loop.
- Browser context: Closing secondary browser tabs, model pop-ups, or new docks that are created during user flow.
- Deleting a user profile image: Deleting a specific image asset uploaded during the test case from the local storage so that the new test doesn’t run into a “file already exists” conflict.
- CI/CD pipeline flag: Export granular failure dumps or performance metrics to external application monitoring (APM) tools like Browserstack immediately after the test throws an exception.
4. afterAll: Worker-Level Resource Teardown
The afterAll hook executes once per worker after all the test cases with a specific scope have finished running.
It’s a global hook that manages post-test teardown, including cleaning system resources, database clean-up, restoring browser state, closing the browser test run or providing dev reports of how successful the test run was.
This hook ensures that any long-running processes are properly terminated after the entire suite finishes.
Example Code:
afterAll(async () => { await browserContext.cleanup();
});What Are The Core Use Cases of afterAll in the Playwright Test Runner?
- Global teardown: Safely terminating active database connection pools, endpoint connections, background server usage or open sockets initiated in beforeAll.
- Memory cache: Wiping down global shared data caches in the test file or tearing down database functions in the worker to not let the test script interfere with the next test file.
- Generate report: Generate unified performance summaries or trace reports that summarise the entire test file execution result for the developer.
- Microservices end-to-end auditing: Shutting down a local backend server connected via HTTP and porting to external test requests for functional graphical testing.
- IoT Data Stream testing: Disconnecting from active message broker subscriptions (like MQTT or Kafka) that were continuously funnelling simulated device data to the frontend UI test.
Use Cases: When to Use Each Playwright Hook
Each hook has its own use case, and understanding when to use them is crucial for effective test management:
- BeforeAll: Use for global setup tasks that are expensive or time-consuming, such as launching a browser or setting up a test database.
- BeforeEach: Use for tasks that should run before every test, like navigating to a specific page, resetting session data, or logging in as a user.
- AfterEach: Use for test-specific cleanup, such as clearing cookies, closing tabs, or resetting state changes made during the test.
- AfterAll: Use for global teardown tasks, like closing browsers, cleaning up server connections, or logging results after all tests have finished.
Code Examples: Implementing Hooks in Playwright
Let’s see how these hooks are implemented in a typical Playwright test suite.
beforeAll(async () => { await browserContext.launch();
});
beforeEach(async () => {
await page.goto(‘https://example.com’);
});
afterEach(async () => {
await page.reload();
});
afterAll(async () => {
await browserContext.close();
});This example demonstrates how to use hooks for launching a browser, navigating to a page before each test, resetting the page after each test, and cleaning up after all tests are done.
Read More: Async/Await in Playwright
Playwright Test Files vs Workers: How Are They Different to Each Other
A major confusion that people run into is whether Playwright individually runs test cases within a particular file or groups them under a global setup or file configuration known as a worker.
Many developers assume that one test file equals one worker process or that tests in the same file share memory dynamically. In Playwright, this is a common misconception that leads to flaky tests.
A test file is a group of individual UI test cases, while a worker is an independent OS tool that executes multiple files concurrently. Every worker has its own browser context, server connection, browser cookies, and data storage. Let’s learn more about the difference in detail.
| Aspects | Test File | Playwright Worker Process |
|---|---|---|
| What it is | Logical, physical file to group test cases and hooks. | Isolated, independent operating system to speed up parallel test execution |
| Concurrency | Tests within a single file run, executing one after another. | Spins up multiple workers to run at the same time. |
| State & Memory | Tests inside the same file share the same file-level variable scope | Each worker has its own memory heap, node process, and browser binaries. |
| Hook Boundaries | A beforeAll defined at the top of a file applies only to the tests within that specific file. | A `beforeAll` runs exactly once when the worker boots up to process that specific file. |
Now comes a question: how do you group related tests within a single file to execute them sequentially without cross-contaminating each other?
For example, if you want multiple tests in a file to check a cashout web flow, but some need “guest user” setup and others need “authorised customer portal” setup, what would you do?
If you use a global, file-level test hook, every single test is forced into the exact same browser configuration, causing massive end-user problems.
This is exactly where test.describe() function of the Playwright test runner comes in. It acts like a structural bridge between test files and localised hook execution.
What is test.describe?
Testers commonly use test.describe to group similar test cases into a singular test file. You can declare test hooks within your “test.describe” function, which will help locally execute your test cases faster.
Test.describe is a function that can be inherited locally or globally using a global class declared outside the test block as well. Which means, test.describe can access file-level test hooks as well as test hooks declared within a particular test case.
Test.describe is a logical function that groups similar tests together, like screen size tests, button position or responsiveness, page load tests, login or authentication flow, and so on. Having this functionality gives you a better idea of how you want to chronologically test your web pages without running into any fatigue or flaky tests.
In the execution order, the beforeEach() hook always executes before local test.describe() to create environmental configuration and device setup for the incoming test. Conversely, local afterEach hooks execute before global afterEach to destroy resources used during local testing for a smooth cleanup.
Here is the code you can refer to if you want to use the test.describe within your test case.
Playwright Global Setup vs. Fixtures vs. Page Object Model: Don’t Confuse with Test Hooks
Navigating when to use standard hooks versus advanced Playwright functions can be challenging.
While Playwright hooks help in setting up tests and tearing down the state for a new app state later on, functions like global setup build initial configurations that remain unchanged, and the page object model represents actual UI elements in the testing environment.
Let’s check how global setup, fixtures and page-level models are different and shouldn’t be confused with test hooks
| Playwright | Global Setup & Teardown | Playwright Fixtures | Playwright Page Object Model (POM) |
|---|---|---|---|
| Architectural Focus | Environment configuration and allocating global variables | Dependency injection and lifecycle (page, browser) of the current test | UI element testing and action encapsulation |
| Execution Phase | Runs once before the worker pool spawns; teardown runs after all workers finish. | Triggered on-demand dynamically as per test case requirements for different components. | Instantiated procedurally inside test hooks for unit testing. |
| Scope & Boundaries | Global runner level (Cross-worker, cross-file). | Can be scoped per individual test or individual browser | Component or page level (Scoped locally for test case instance) |
| State Awareness | Has no access to active browser context, page target or live DOM instances | Fully manages browser context, tabs, custom contexts and cleanups | Bound directly to a specific page instance passed in the private member function. |
| Primary Use Case | Initialising environment configurations, mock servers or auth tokens | Abstracting complex, reusable setup flows (testing the sign-in page) | Mapping web element selectors with test interactions |
| Framework Benefit | Eliminates time-consuming configuration tasks | Replaces verbose, repetitive beforeEach with self-cleaning | Prevents script breakage by separating test conditions from changing UI. |
Avoiding Common Pitfalls with Playwright Hooks
Using hooks incorrectly can lead to issues such as:
- Unintended side effects: Ensure that each test starts with a clean environment to avoid tests interfering with one another.
- Performance issues: Avoid performing heavy tasks like browser launches or data setups in BeforeEach if they can be done once in BeforeAll.
- Skipping cleanup: Always make sure that resources are cleaned up in AfterEach or AfterAll, especially when working with external services or persistent data.
Conclusion
Test hooks allow you to test the accessibility and performance of your browser web application in an organised and efficient way.
Grouping similar tests with similar context and saving browser configuration for every test file eliminates any scope of code duplication or test case bleeding. These text hooks manage the “Before” and “after” arrangements to create a test environment that automates cross-browser testing.
By integrating with a platform like “Browserstack Automate”, you can scale your tests, debug more effectively and streamline the entire testing process across multiple browsers and devices.
