Skip to content

Learning proposal: (939ea840) #3906

Description

@fro-bot

Source: merge commit 939ea840d348c13c4296b721768af26bd64eeeb3 — review-heavy (2 substantive review rounds).

Proposed learning: module-level constants are invisible to mutation testing; hoist them into functions to make the seams observable. The reviewer identified the load-bearing move in this change as converting six module-level path constants into functions. The mechanism is specific and generalizable: a top-level const initializer runs once per Stryker worker process, so mutants of its literal segments are pinned for the lifetime of that process and are never attributed to any individual test. They surface as survivors that no amount of test quality can kill. Making the value a function call moves the mutated code into the test's own execution frame, where it can be observed and killed. The review notes this is the same class of finding — and the same fix — as a previously reviewed script, which makes it a recurring trap rather than a one-off.

Second, independently reusable: the discipline around suppression directives. Survivors went 22 to 3 to 1, and the remaining ones were deleted rather than silenced. A main() that was pure indirection with a single caller was inlined, which removes its block-statement mutant entirely. A readFile(path, 'utf8') whose result was immediately .toString()-ed had the 'utf8' argument removed rather than hedged behind a directive. The discriminating detail worth writing down: only the JSON.parse-fed read needed that treatment, because JSON.parse silently coerces a Buffer while .split/.replaceAll throw — the other readFile(path, 'utf8') calls in the same file were already killable. A first pass that restructured a pathExists try/catch into .then() purely to make a directive placeable was reverted, on the principle that reshaping code to accommodate a suppression turns a mutation guard into theater.

Third: the framing that ties both together. A build script that grades its own output with its own comparator is a closed loop, and the only way to open it is to make the seams observable from outside. That is the general statement of the problem this change solved; the constant-to-function conversion and the directive deletions are both instances of it.

Suggested capture: docs/solutions/testing/ or docs/solutions/best-practices/, alongside the existing note on enumerating mutator variants before adding a Stryker directive. Scope: why top-level consts produce unkillable mutants under Stryker's per-worker model, the function-hoisting fix, the Buffer-coercion asymmetry that determines which 'utf8' arguments are safe to drop, and the delete-the-code-or-add-a-seam-but-never-reshape-to-suppress ordering.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    learning-proposalCandidate learning proposed from a multi-round-review PR

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions