Skip to main content

Coverage and mutants, a ShirePath lab study

Full coverage, surviving bugs

A coverage tool says which lines ran. A mutation tool changes a line and asks whether any test noticed. Below is a small ownership check and the tests that reach every line of it. Turn on a mutant to break the function one way, and see which tests catch it.

Line coverage

100%

3 of 3 lines run

Mutants caught

–

Turn on a mutant to test the tests

The function

export async function getFormForUser(db: Db, formId: string, userId: string) {
  const row = await db.findOne({ id: formId, ownerId: userId });
  if (!row) return null;
  return { id: row.id, title: row.title, dueOn: row.dueOn };
}

The tests

  • Areturns the form to its owner

    The fake: Returns the one row it holds, whatever the query asks for.

    it("returns the form to its owner", async () => {
      const db = { findOne: async () => FORM };
      expect(await getFormForUser(db, "f1", "u1")).toEqual({
        id: "f1", title: "Fall intake", dueOn: "2026-11-01",
      });
    });
  • Breturns null when there is no form

    The fake: Returns nothing, whatever the query asks for.

    it("returns null when there is no form", async () => {
      const db = { findOne: async () => undefined };
      expect(await getFormForUser(db, "missing", "u1")).toBeNull();
    });
  • Casks the database once

    The fake: Counts calls and returns the one row it holds.

    it("asks the database once", async () => {
      const findOne = vi.fn(async () => FORM);
      await getFormForUser({ findOne }, "f1", "u1");
      expect(findOne).toHaveBeenCalledTimes(1);
    });

Mutants

What it shows

Tests A and B hand the function a fake database that returns its row whatever the query asks for. So the line that builds the query runs, coverage counts it, and nothing checks it. The three mutants that break the ownership filter are the ones a user would care about most, and only test D sees them.

Test C counts database calls. The only mutant it catches alone is the one that changes nothing a user can see.

This check was written fresh for the study, but the pattern is from the audit: a read-by-id function with an ownership filter showed full coverage, and three hand-written mutants that dropped or corrupted the filter survived all 746 tests that ran it.