So Long, “beforeEach”? New Testing Pattern in JavaScript

August 26, 2025

Tests in TypeScript or JavaScript often run setup code inside hooks like “beforeEach”:

Sample code demonstrating how to setup code using "beforeEach" hook.
Developers must take care to get the setup correct, especially in large or nested test suites.

  • Many scopes (“describe”, “beforeEach”, “it”) read and mutate the test fixtures. The setup must reset the fixtures before each test. Test authors must be careful to not accidentally shadow a variable declared in a higher scope.
  • Asynchronous setup (await createTeam()) often requires splitting variable initialization from declaration. Typescript hacks (“= null!”) paper over this issue.

Some test runners now provide fixtures-per-test. We can re-write our test suite as the following:

sample code demonstrating how to use Vitest test fixtures.

This test setup addresses our original pain points, and adds a few new features:

  • Fixtures are only created if a test or another fixture depends on it (e.g. async ({ player }) => { … }).
  • Test files can share fixtures from a common base “it”.
  • Fixtures by default are created and cleaned up for every test. Fixtures can opt into being shared across an entire test file or test worker. This is useful for something that takes a long time to setup, like an api server or a containerized database.

Further Considerations:

  • Consider how much to put in the test context. Sometimes it makes sense to put all the test cases in the context. Other times it makes sense to share a base fixture that each test modifies.
  • Test context and the setup hooks can be used together. While you can move things like mock cleanup to an “auto” fixture, it may be more convenient to to continue to use “beforeEach” and the like.

For further reading, check out the Vitest and Playwright documentation.