Skip to content

date: 2026-06-18 tags: [mutation-testing, pest, assertions] status: active graduated_to:

Assert the whole render with toBetoContain leaves array-element and blank-line mutants alive

Symptom — the diff-scoped mutation gate flagged survivors on a method that builds a multi-line string from an array of lines: RemoveArrayItem (drop a line — including a '' blank-line separator) and EmptyStringToNotEmpty (mutate a '' separator to 'PEST Mutator was here!'). The tests asserted the output with a chain of ->toContain(...), which all still passed against the mutated output.

Root causetoContain only proves a substring is present. It cannot detect a removed line, a changed blank-line separator, or a reordering — so any mutant that preserves the asserted substrings survives. The separators and the exact line set are invisible to it.

Fix — assert the complete rendered string with a single toBe, which pins every line, label, and blank-line separator at once (see tests/Feature/Mcp/GetTaskContextToolTest.php, the get_task_context render test). One exact-match assertion killed eight array/blank-line mutants that a dozen toContains missed.

Guard — for a function whose behaviour is the exact text/structure it emits, assert the full output with toBe, not field-by-field toContain. Reserve toContain for "this fragment appears somewhere", not "the output is shaped like this". Candidate to graduate into docs/agents/mutation-testing.md if it recurs.