Tests are the contract: how I trust code an AI wrote
Code written by an AI looks confident. It compiles, it has tidy names, it even has comments. None of that tells you it does what the client asked. The only thing that does is a test that fails when the behavior is wrong.
Test the behavior, not the implementation
I care about what a visitor can do: add a product to the cart, submit a form, pay. End-to-end tests describe exactly that, and they do not care how the code underneath was written, by me or by a model. That makes them the ideal referee for AI-assisted work.
test("visitor can request a website audit", async ({ page }) => {
await page.goto("/website-audit/");
await page.fill("[name=site]", "example.com");
await page.fill("[name=name]", "Test User");
await page.fill("[name=email]", "test@example.com");
await page.click("button[type=submit]");
await expect(page.locator(".form__status")).toContainText("review");
});
Write the test first when the stakes are high
For checkout flows, forms and payments, I write the test before I ask for the code. The test is the specification. If the generated code passes, I read it and keep it. If it fails, the failure message tells me exactly what to say in the next prompt.
Do not let the model grade its own homework
A model asked to write both the code and the tests will happily write tests that pass. I review the assertions themselves: do they check the real outcome, or just that a function was called? A test that cannot fail is decoration.
Cover the ugly paths
- Empty and invalid input.
- Slow networks and failed requests.
- Mobile widths and keyboard-only navigation.
- The thing that broke last time.
Document what the tests protect
Short technical documentation next to the tests pays for itself. It tells the next developer, or the next model, which behaviors are promises and which are accidents.
AI made writing code faster. It did not make being wrong cheaper. Tests are how you keep the speed without paying for the mistakes.