How Do You Set Hooks Before Or After Each Test Globally in Playwright Test?
This was a recent question that was asked in the Playwright Discord channel.
I want to do some kind of "reset" in a specific table after every spec. I could simply achieve it by setting up anafterAll
in each spec but this is equal repeating the same line of code a lot (imagine 100 spec files). So what my team did a while ago was adding hooks in ourtest.ts
file. We really thought it was working like we imagined. "Oh, since we defined these hooks in the test file, it will act like a global hook". Little did we know that this is not true.
I know pw has global setup and teardown, but AFAIK it will only run once, before everything and after everything. We really need a setup that runs after every spec file instead. Could be something after everydescribe
too but I don't think we can override it. Is this already being discussed / a requested feature ? If not, should I do it or is there a way / hack I can achieve the desired behaviour? Thanks in advance! Really keen to read some suggestions.
The first answer in the thread was a link to Automatic Fixtures from Tally Barak.
The docs say "Automatic fixtures are set up for each test/worker, even when the test does not list them directly. To create an automatic fixture, use the tuple syntax and pass { auto: true }
."
This means that as long as the fixture is imported at the top of the file, whatever is inside the fixture will run automatically without having a reference to it within the test()
block.
xxxx.spec.ts
file completes. As far as I am aware today, this isn't possible. The solution below will cover the use case running an afterEach after every test run.I've only implemented a few fixtures so I wanted to take a stab at creating a solution for this problem. An example can be found below. This fixture extends the test
class, and allows code to be added before await use()
which will be executed before the test code, and code added after await use()
will be executed after the test code.
// lib/fixtures/hook.ts
import { test as base } from "@playwright/test";
export const test = base.extend<{ testHook: void }>({
testHook: [
async ({}, use) => {
console.log("BEFORE EACH HOOK FROM FIXTURE");
// Any code here will be run as a before each hook
await use();
console.log("AFTER EACH HOOK FROM FIXTURE");
// Put any code you want run automatically after each test here
},
{ auto: true },
],
});
export { expect } from "@playwright/test"; //Exporting 'expect' from the base test so you have access in your spec.ts file.
The spec that uses the fixture is below. I am using console.log()
here so we can see when each line of code gets run. Notice the one change I made in this spec file was where I am importing the test
and expect
from the fixture we created above ../../lib/fixtures/hook
.
// tests/ui/hooks.spec.ts
import { test, expect } from "../../lib/fixtures/hook";
test.describe("Highest Level", () => {
let x: number;
x = 1;
test.beforeAll(() => {
console.log("--Hello before all tests--");
});
test.afterAll(() => {
console.log("--Hello after all tests--");
});
test.afterEach(() => {
console.log("<<Hello after each test");
});
test.beforeEach(() => {
console.log(">>Hello before each test");
});
test.afterAll(() => {
console.log("Hello after all tests");
});
test("One", async ({ page }) => {
expect(x).toBe(1);
});
test("One v2", async ({ page }) => {
expect(x).toBe(1);
});
test("Two", async ({ page }) => {
expect(x).toBe(1);
});
test("Two v2", async ({ page }) => {
expect(x).toBe(1);
});
});
The output from the test results are shown below.
The pull request for these changes can be seen and found here.
Wrapping up if you have a use case where you need execute code or capture information before or after each test utilizing a fixture like this can be really useful.
⚠️ One risk to consider, is if you are modifying test data via database seed scripts or reset scripts in a before or after hook and running more than 1 shard
or more than 1 worker
, it's possible that the data modification could affect other tests that are running.
Thanks for reading! If you found this helpful, reach out and let me know on LinkedIn or consider buying me a cup of coffee. If you want more content delivered to you in your inbox subscribe below, and be sure to leave a ❤️ to show some love.