Rules Hub

Coding Rules Library

← Back to all rules
fullstack ruleStack: testing
testingside-effectscleanupmockingvitestjestglobal-state

Wrap global mutations in try-finally during tests

When a test modifies the global environment (like prototypes or global variables), use a try-finally block to guarantee the environment is restored even if assertions fail.

PR: Feat/FCK-2200 - Polyfill isWellFormed for Safari slugs #341Created: Dec 7, 2025

Bad example

Old codets
1it('works without String.prototype.isWellFormed', () => {
2 const original = String.prototype.isWellFormed;
3
4 // Mutate global environment
5 Reflect.deleteProperty(String.prototype, 'isWellFormed');
6
7 // If this assertion fails, the cleanup below never executes
8 expect(myPolyfillUtil('input')).toBe('safe');
9
10 // Manual cleanup at the end is unsafe
11 String.prototype.isWellFormed = original;
12});

Explanation (EN)

The cleanup logic is placed after the assertions. If an assertion fails, the test function exits immediately, leaving the global `String.prototype` modified. This pollutes the environment and causes subsequent tests to fail unpredictably.

Objašnjenje (HR)

Logika za čišćenje (cleanup) nalazi se nakon asercija. Ako asercija ne prođe, test funkcija se odmah prekida, a globalni `String.prototype` ostaje modificiran. To zagađuje okolinu i uzrokuje nepredvidive padove u idućim testovima.

Good example

New codets
1it('works without String.prototype.isWellFormed', () => {
2 const descriptor = Object.getOwnPropertyDescriptor(String.prototype, 'isWellFormed');
3
4 try {
5 // Mutate global environment safely
6 Reflect.deleteProperty(String.prototype, 'isWellFormed');
7
8 expect(myPolyfillUtil('input')).toBe('safe');
9 } finally {
10 // Cleanup ensures environment is restored even if test fails
11 if (descriptor) {
12 Object.defineProperty(String.prototype, 'isWellFormed', descriptor);
13 }
14 }
15});

Explanation (EN)

The test wraps the mutation and assertions in a `try-finally` block. This guarantees that the original environment is restored in the `finally` clause, regardless of whether the test passes, fails, or throws an error, keeping tests isolated.

Objašnjenje (HR)

Test omata mutaciju i asercije u `try-finally` blok. Ovo garantira da će se originalna okolina vratiti u prvobitno stanje unutar `finally` dijela, bez obzira na to hoće li test proći, pasti ili baciti grešku, čime testovi ostaju izolirani.

Notes (EN)

This pattern is preferred over `afterEach` for one-off, invasive mutations (like deleting a prototype method) to keep the test self-contained and avoid accidental pollution if lifecycle hooks are misconfigured.

Bilješke (HR)

Ovaj uzorak je poželjniji od `afterEach` za jednokratne, invazivne mutacije (poput brisanja metode prototipa) kako bi test ostao samostalan i kako bi se izbjeglo slučajno zagađenje ako su 'lifecycle' kuke pogrešno konfigurirane.