post: An Unsafe Autofix Can Change What a Test Covers_□×

An Unsafe Autofix Can Change What a Test Covers

The serializer has a test for an array hole:

// biome-ignore lint/suspicious/noSparseArray: an array hole is the case under test
expect(safeStringify([1, , 3])).toBe('[1,"[undefined]",3]');

Replacing the hole with an explicit undefined leaves the expected output unchanged. It also removes the case the test was meant to cover.

Biome offers that replacement as an unsafe lint fix. It isn’t an ordinary formatting change, and it doesn’t happen with --write alone.

Absent and undefined aren’t the same input

The middle position in [1, , 3] has no own element. In [1, undefined, 3], the element exists and its value is undefined.

Reading either position by index produces undefined in these arrays, but array methods don’t always treat them alike.

Array.prototype.map skips empty slots. If the serializer mapped a sanitizing function over the sparse array, the callback wouldn’t run for the hole. The hole would remain in the result, and JSON serialization would render it as null.

The implementation instead uses an indexed loop. It reads each position and passes the value through the sanitizer, producing the logger’s explicit "[undefined]" marker for the hole.

With that implementation, both inputs produce:

[1,"[undefined]",3]

The output is intentionally the same. The inputs still need separate tests because a future implementation change could handle one correctly and the other incorrectly.

The original comment blamed the wrong command

The comment above the test said biome check --write would replace the hole. Without the suppression, that command reports the rule violation and leaves the sparse array intact.

Adding --unsafe enabled the replacement:

[1, , 3]  →  [1, undefined, 3]

Biome classifies the fix as unsafe because it can change behavior. The corrected comment needs to name the command that opts into that change.

I like having the distinction in the tooling. A normal cleanup command should not quietly make this decision for the test. An explicit unsafe-fix pass still needs review, in source files as well as tests.

The suppression is narrow and has a concrete reason: this particular hole is deliberate test input.

A passing rewritten test can lose its purpose

After the replacement, the assertion still passes against the indexed-loop implementation. It now checks explicit undefined, not an absent element.

A later refactor from the loop to map could keep that rewritten test passing while changing sparse-array output. The original hole test would catch the difference.

Coverage numbers don’t settle this. The line can still execute, and some coverage measures may remain unchanged, without preserving the original input case. There is no general guarantee that every coverage metric would be identical.

The review question is simpler: does this test still contain the input it was written to exercise?

Odd syntax in a fixture can be a mistake, but it can also be the entire reason the test exists. Before accepting an autofix, read the assertion and the reason for the unusual input together.

Here, the extra comma isn’t clutter. Removing it removes the sparse-array case.

Sources

I’d appreciate a follow. You can subscribe with your email below. The emails go out once a week, or you can find me on Mastodon at @[email protected].

START
llbbl.exeprojects/posts/experiments/subscribe.dlg
© 2026v1.0.0