Refining Test Automation Setup with Playwright Project Dependencies
The setup phase prepares the test environment before test execution. It includes tasks like initializing test data, configuring the application under test (AUT), and establishing necessary preconditions for successful test scenario execution. This phase is crucial to ensure tests start from a predefined state, reducing variables that may lead to inconsistent or unreliable test outcomes.
The teardown phase cleans up the test environment by closing connections, releasing resources, and resetting the system to its initial state. It is crucial for maintaining the integrity of the test environment and ensuring that each test case is isolated from others. Without teardown, the changes or side effects of one test case might affect subsequent test cases.
Setup and teardown are important in test automation. They establish a controlled testing environment and are essential to popular testing frameworks. They improve the reliability, maintainability, and scalability of automated tests. Modern test automation frameworks have built-in features for defining setup and teardown methods. For instance, Playwright uses isolated Browser Contexts for independent tests, ensuring no interference and a fresh start for each test. Tests can also use storage state from other tests to simulate a logged-in state, which is especially useful for authentication or authorization scenarios.
While the traditional approach in test automation is to use setup and teardown for logging in and out of the application, Playwright also supports this. In March 2023, Playwright introduced changes that allow traces to be used in global setup or teardown and captured in the HTML report.
To set up and tear down tests, we will use project dependencies in Playwright. A Playwright project is a group of tests that run with the same configuration. We will create a separate project in Playwright’s config for these tasks. The project’s dependencies are a list of projects that must run before the tests in the current project. If any of the projects in the list fail, the current project will not run because it depends on their success.
The benefits of using project dependencies for setup and teardown are:
- Global setup and teardown can be traced and recorded, which can be helpful for debugging and troubleshooting.
- Fixtures can be used in global setup and teardown, allowing you to share data between the setup and teardown code and your tests.
- Project dependencies are declarative, making it easy to see which projects depend on each other.
When setting up a project, use testDir
to specify the test directory and testMatch
to run only setup.ts
tests. Configure baseURL
in playwright.config.ts
when working with shared URLs.
import { test as setup, expect } from '@playwright/test';
import { STORAGE_STATE } from '../playwright.config';
setup('Login to Saucedemo', async ({ page }) => {
await page.goto('/');
await page.locator('[data-test="username"]').click();
await page.locator('[data-test="username"]').fill('standard_user');
await page.locator('[data-test="password"]').click();
await page.locator('[data-test="password"]').fill('secret_sauce');
await page.locator('[data-test="login-button"]').click();
await page.context().storageState({ path: STORAGE_STATE });
});
In the setup script, we import test as setup
to define a setup method that runs before each test method. In this method, we can create the necessary resources or objects required for the tests, or set up the necessary state. The setup project writes the storage state into a json
file and exports a constant called STORAGE_STATE
. This constant is used to share the location of the storage file between different projects. When configuring the setup project, we can specify the teardown project, which will automatically be called once all tests in the project have been executed.
Similarly, we can use teardown
to define a method called after all the test method. Inside the teardown method, we can clean up any resources or objects created by the setup method, or reset any changed state.
export const STORAGE_STATE = path.join(__dirname, 'playwright/.auth/user.json');
export default defineConfig({
use: {
baseURL: '<https://www.saucedemo.com>',
},
projects: [
{
name: 'setup',
testDir: './',
testMatch: '*.setup.ts',
teardown: 'teardown'
},
{
name: 'teardown',
testDir: './',
testMatch: '*.teardown.ts',
use: {
storageState: STORAGE_STATE,
}
},
{
name: 'saucedemo',
testMatch: '*.e2e.spec.ts',
dependencies: ['setup'],
testIgnore: ['*.setup.ts'],
use: {
storageState: STORAGE_STATE,
},
},
Create a project called saucedemo
that matches all tests with the file name e2e.spec.ts
. Using the use
option, we inform the test to use storageState
as the STORAGE_STATE
variable created in the setup script. The saucedemo
project should depend on the setup
project. The setup
project will have a corresponding teardown
project. During the first text execution, the JSON file will be created and stored. For faster testing during subsequent executions, use [testIgnore]
to skip setup.
My recent publication compiled a comprehensive collection of 100 similar typescript programs. Each program not only elucidates essential Typescript concepts and expounds upon the significance of test automation but also provides practical guidance on its implementation using Playwright. This resource will undoubtedly be valuable if you look deeper into similar topics.