Skip to main content
Building with AI (Vibe Coding)

⏱ About 20 min20 XP

Testing What You Built

A function that works on the three inputs you tried is not a function that works. It is a function that has not failed yet. Testing is the systematic practice of deliberately trying to make your software fail, so you discover those failures before your users do. If that framing sounds adversarial — good. It is supposed to. You are your own most useful critic.

What a Test Actually Is

A test is a small, executable claim about the behavior of your software. It has three parts: 1. Arrange: set up the inputs and conditions. 2. Act: call the code under test. 3. Assert: check that the output or state matches what you expect. If the assertion is true, the test passes. If it is false, the test fails — and now you have precise, reproducible information about what went wrong. Example: you have a function called calculateDiscount(price, percent) that should return the discounted price. Arrange: price = 100, percent = 20. Act: result = calculateDiscount(100, 20). Assert: result equals 80. This one test encodes a claim: 'for a $100 item with a 20% discount, the function returns $80.' If tomorrow someone changes the function and that claim becomes false, the test fails immediately and tells you exactly where to look.

Tests Are Executable Specifications

A test suite is not just a safety net — it is documentation that cannot go stale, because it runs. Every passing test is a verified statement about what your software actually does right now. This is why experienced engineers add tests before fixing bugs: the test proves the bug exists, then proves the fix worked.

There are several categories of tests worth knowing: Unit tests target the smallest isolatable piece of code — a single function or method — in isolation from the rest of the system. They run fast and tell you precisely which unit is broken. Integration tests check that multiple components work correctly together. A unit test might verify that a database query function parses its result correctly; an integration test would verify that the function actually retrieves the right records from a real (or realistic) database. End-to-end tests simulate a real user journey through the entire application — clicking buttons, submitting forms, reading results. They are the most realistic and the slowest. For a builder working with AI-generated code, unit tests are your highest-leverage starting point: they are fast to write, fast to run, and they isolate problems precisely.

Choosing What to Test

You cannot test everything. Choosing well matters. Prioritize tests for: Boundary cases: what happens at the edge of a valid range? If a function accepts integers 1–100, test 0, 1, 100, and 101. Error handling: what happens with invalid input? Does the function raise a clear error or silently return garbage? Known failure modes: if AI generated the code, what are the categories of mistakes AI tools commonly make in this kind of code? (Off-by-one errors, timezone mishandling, and SQL injection are frequent.) Business-critical paths: what behavior, if wrong, causes real harm to users or the business? A test that only verifies the obvious 'happy path' — the input you already know works — is the weakest possible test. Push your inputs toward the edges and the unexpected.

Flashcards — click each card to reveal the answer

Test the Code You Didn't Write Too

When an AI generates code for you, write at least three tests before you consider it done: one happy path, one boundary case, and one invalid input. If you cannot easily write those tests, that is a signal the function's design may be unclear.

A function isEven(n) that returns true for even integers and false for odd integers is tested only with n=4 and n=7, both of which pass. A student claims the function is correct. What is the most significant flaw in this claim?

Why is a failing test more valuable than a passing test when you are debugging?

Write Before You Read

  1. Step 1: Ask an AI assistant to generate a function calculateTip(billAmount, tipPercent) that returns the tip amount as a number rounded to two decimal places.
  2. Step 2: BEFORE reading the AI's code in detail, write five tests covering: a normal case, the boundary where tipPercent=0, a negative billAmount, a very large billAmount (e.g. 9999999.99), and a non-numeric input.
  3. Step 3: Run all five tests against the AI's code.
  4. Step 4: For each failing test, read the relevant part of the AI's code to understand why it fails.
  5. Step 5: Fix the failures. Did the AI's code handle all five cases correctly? Document each fix you made.