Naming the kinds of cases and "one test per behaviour" gets tests that document the function, not one big happy path.
GPT-6 Luna
PassedGraded 5.0 of 5 on average (lowest 5).
```js
import { describe, expect, it } from "vitest";
import { splitBill } from "./splitBill";
describe("splitBill", () => {
it("splits the total evenly with no tip", () => {
expect(splitBill(60, 3)).toBe(20);
});
it("includes the tip before splitting", () => {
expect(splitBill(100, 4, 20)).toBe(30);
});
it("rounds each person's share to two decimal places", () => {
expect(splitBill(10, 3)).toBe(3.33);
});
it("allows a zero total", () => {
expect(splitBill(0, 2)).toBe(0);
});
it("allows one person", () => {
expect(splitBill(25, 1)).toBe(25);
});
it("rejects zero people", () => {
expect(() => splitBill(10, 0)).toThrow(
"people must be a whole number above 0",
);
});
it("rejects negative people", () => {
expect(() => splitBill(10, -1)).toThrow(
"people must be a whole number above 0",
);
});
it("rejects fractional people", () => {
expect(() => splitBill(10, 1.5)).toThrow(
"people must be a whole number above 0",
);
});
it("rejects non-numeric people", () => {
expect(() => splitBill(10, "2")).toThrow(
"people must be a whole number above 0",
);
});
it("rejects a negative total", () => {
expect(() => splitBill(-1, 2)).toThrow("total can't be negative");
});
});
```1 message on Pro · $0.0001 to run · 3.8 s · 400 tokens out
Claude Sonnet 5.5
PassedGraded 5.0 of 5 on average (lowest 5).
```js
import { describe, it, expect } from "vitest";
import { splitBill } from "./splitBill";
describe("splitBill", () => {
describe("normal cases", () => {
it("splits the total evenly between people", () => {
expect(splitBill(100, 4)).toBe(25);
});
it("returns the full total for one person", () => {
expect(splitBill(80, 1)).toBe(80);
});
it("applies the tip percentage before splitting", () => {
expect(splitBill(100, 4, 20)).toBe(30);
});
it("defaults the tip to 0 when omitted", () => {
expect(splitBill(60, 3)).toBe(splitBill(60, 3, 0));
});
it("rounds the result to two decimal places", () => {
expect(splitBill(100, 3)).toBe(33.33);
});
it("rounds up when the third decimal is 5 or more", () => {
expect(splitBill(20, 3)).toBe(6.67);
});
it("handles decimal totals", () => {
expect(splitBill(45.5, 2)).toBe(22.75);
});
});
describe("edge cases", () => {
it("returns 0 when the total is 0", () => {
expect(splitBill(0, 5)).toBe(0);
});
it("returns 0 when the total is 0 even with a tip", () => {
expect(splitBill(0, 5, 20)).toBe(0);
});
it("accepts a tip of 0 explicitly", () => {
expect(splitBill(50, 2, 0)).toBe(25);
});
it("handles a tip over 100 percent", () => {
expect(splitBill(100, 2, 150)).toBe(125);
});
it("handles decimal tip percentages", () => {
expect(splitBill(100, 1, 12.5)).toBe(112.5);
});
it("reduces the amount for a negative tip (discount)", () => {
expect(splitBill(100, 2, -10)).toBe(45);
});
it("handles a very large number of people", () => {
expect(splitBill(1, 1000)).toBe(0);
});
it("handles very large totals", () => {
expect(splitBill(1e9, 4)).toBe(250000000);
});
});
describe("bad input", () => {
it("throws when people is 0", () => {
expect(() => splitBill(100, 0)).toThrow("people must be a whole number above 0");
});
it("throws when people is negative", () => {
expect(() => splitBill(100, -2)).toThrow("people must be a whole number above 0");
});
it("throws when people is a decimal", () => {
expect(() => splitBill(100, 2.5)).toThrow("people must be a whole number above 0");
});
it("throws when people is NaN", () => {
expect(() => splitBill(100, NaN)).toThrow("people must be a whole number above 0");
});
it("throws when people is Infinity", () => {
expect(() => splitBill(100, Infinity)).toThrow("people must be a whole number above 0");
});
it("throws when people is a numeric string", () => {
expect(() => splitBill(100, "4")).toThrow("people must be a whole number above 0");
});
it("throws when people is undefined", () => {
expect(() => splitBill(100)).toThrow("people must be a whole number above 0");
});
it("throws when people is null", () => {
expect(() => splitBill(100, null)).toThrow("people must be a whole number above 0");
});
it("throws when the total is negative", () => {
expect(() => splitBill(-1, 2)).toThrow("total can't be negative");
});
it("checks people before total when both are invalid", () => {
expect(() => splitBill(-1, 0)).toThrow("people must be a whole number above 0");
});
it("returns NaN when the total is NaN", () => {
expect(splitBill(NaN, 2)).toBeNaN();
});
it("returns NaN when the tip is not a number", () => {
expect(splitBill(100, 2, "abc")).toBeNaN();
});
});
});
```1 message on Pro · $0.0160 to run · 7.9 s · 1,442 tokens out
DeepSeek V4.1 Flash
PassedGraded 4.3 of 5 on average (lowest 3).
```js
import { describe, it, expect } from "vitest";
import { splitBill } from "./splitBill";
describe("splitBill", () => {
it("splits the total evenly between people with no tip", () => {
expect(splitBill(100, 4)).toBe(25);
});
it("adds the tip percentage before splitting", () => {
expect(splitBill(100, 4, 20)).toBe(30);
});
it("rounds each share to two decimal places", () => {
expect(splitBill(100, 3)).toBe(33.33);
});
it("treats a missing tip as zero", () => {
expect(splitBill(50, 2)).toBe(25);
});
it("splits a zero total into zero shares", () => {
expect(splitBill(0, 3, 15)).toBe(0);
});
it("gives the whole total to a single person", () => {
expect(splitBill(87.5, 1)).toBe(87.5);
});
it("handles a tip of zero explicitly", () => {
expect(splitBill(60, 3, 0)).toBe(20);
});
it("rounds up a repeating decimal share correctly", () => {
expect(splitBill(10, 3, 0)).toBe(3.33);
});
it("throws when people is not an integer", () => {
expect(() => splitBill(100, 2.5)).toThrow("people must be a whole number above 0");
});
it("throws when people is zero", () => {
expect(() => splitBill(100, 0)).toThrow("people must be a whole number above 0");
});
it("throws when people is negative", () => {
expect(() => splitBill(100, -3)).toThrow("people must be a whole number above 0");
});
it("throws when people is not a number", () => {
expect(() => splitBill(100, "4")).toThrow("people must be a whole number above 0");
});
it("throws when the total is negative", () => {
expect(() => splitBill(-50, 2)).toThrow("total can't be negative");
});
it("accepts a total of exactly zero with valid people", () => {
expect(splitBill(0, 1)).toBe(0);
});
});
```1 message on Pro · $0.0007 to run · 1.8 s · 555 tokens out
GLM 5.3
PassedGraded 5.0 of 5 on average (lowest 5).
```js
import { describe, expect, it } from "vitest";
import { splitBill } from "./splitBill";
describe("splitBill", () => {
it("splits a total evenly with no tip", () => {
expect(splitBill(100, 4)).toBe(25);
});
it("applies the tip before splitting", () => {
expect(splitBill(100, 4, 10)).toBe(27.5);
});
it("rounds the share to two decimal places", () => {
expect(splitBill(10, 3)).toBe(3.33);
});
it("rounds up to two decimal places when needed", () => {
expect(splitBill(20, 3)).toBe(6.67);
});
it("returns zero for a zero total", () => {
expect(splitBill(0, 5)).toBe(0);
});
it("accepts a fractional tip percentage", () => {
expect(splitBill(200, 2, 12.5)).toBe(112.5);
});
it("throws when people is not an integer", () => {
expect(() => splitBill(100, 2.5)).toThrow("people must be a whole number above 0");
});
it("throws when people is zero", () => {
expect(() => splitBill(100, 0)).toThrow("people must be a whole number above 0");
});
it("throws when people is negative", () => {
expect(() => splitBill(100, -2)).toThrow("people must be a whole number above 0");
});
it("throws when people is NaN", () => {
expect(() => splitBill(100, NaN)).toThrow("people must be a whole number above 0");
});
it("throws when people is a non-number string", () => {
expect(() => splitBill(100, "4")).toThrow("people must be a whole number above 0");
});
it("throws when total is negative", () => {
expect(() => splitBill(-10, 2)).toThrow("total can't be negative");
});
});
```1 message on Pro · $0.0024 to run · 1.9 s · 507 tokens out