100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Software Testing

Writing Your First Test First

Learn what it means to specify behavior with a failing test before any implementation exists, and how to pick a good starting test case.

TDD in PracticeBeginner8 min readJul 10, 2026
Analogies

What It Means to Write a Test First

Writing a test first means specifying the expected behavior of a piece of code before that code exists, then writing the minimum implementation needed to satisfy the specification. Instead of coding a function and checking afterward whether it seems to work, you write an assertion like expect(add(2, 3)).toBe(5) against a function that does not exist yet, watch it fail, and only then write add(). This reverses the usual order: you design the interface and behavior from the caller's perspective before worrying about implementation details.

🏏

Cricket analogy: A coach doesn't design a batsman's stroke after the ball is bowled — he sets a target shot, like Virat Kohli drilling a cover drive against a specific line and length, before facing that delivery in a match, just as a test defines the expected outcome before the code exists.

The Red-Green-Refactor Cycle

Test-first development follows a tight loop: write a failing test (red), write the simplest code that makes it pass (green), then improve the code's structure without changing its behavior (refactor). Each cycle should take minutes, not hours. Seeing the test fail first is not a formality — it proves the test can actually detect a wrong or missing implementation, which is the whole point of writing it before the code. A test that passes immediately without an implementation is testing nothing.

🏏

Cricket analogy: A bowler practicing yorkers first bowls a full toss to confirm the stump camera and speed gun are actually recording (red), then adjusts run-up and release to land the yorker (green), and finally smooths the action for consistency (refactor).

javascript
// Step 1: RED — write the test before the implementation exists
test('add sums two positive integers', () => {
  expect(add(2, 3)).toBe(5); // add() is not defined yet — this fails
});

// Step 2: GREEN — write the minimum code to pass
function add(a, b) {
  return a + b;
}

// Step 3: REFACTOR — clean up once green, tests stay untouched
const add = (a, b) => a + b;

Choosing Your First Test Case

The best first test for a new piece of behavior is usually the simplest possible case that forces you to write real code rather than a hardcoded constant. For a function that sums a list, testing sum([]) equals 0 is weak because it can be satisfied by returning 0 unconditionally; testing sum([2, 3]) equals 5 forces an actual accumulation. Picking a test that is too complex up front, with edge cases and error handling all at once, makes the first red-green cycle slow and defeats the purpose of small, fast increments.

🏏

Cricket analogy: A net bowler warming up a new batter starts with a straight half-volley, not a googly with the second new ball, because the first ball needs to force a real shot, not a lucky leave.

A useful rule of thumb: if your first test can pass by returning a hardcoded literal, it hasn't yet earned the right to be called a test of behavior — write a second case that forces a real implementation, a technique known as triangulation.

Common Beginner Mistakes

New practitioners often write the test and the implementation in the same breath, which erases the value of watching red turn to green, or they write a large integration test as their 'first test' when a focused unit test would isolate the behavior far better. Another frequent mistake is testing framework or language behavior instead of your own logic — for example, asserting that JSON.parse works correctly rather than testing how your code handles the parsed result. The test should exercise a decision your code makes, not a guarantee the platform already provides.

🏏

Cricket analogy: A rookie umpire who signals a boundary before the ball actually crosses the rope is skipping the verification step, just like writing code and test together skips proving the test can fail.

If you never see your test fail, you cannot trust that it is testing anything at all — a test that is green from the moment it is written may be checking the wrong thing, or nothing.

  • Writing a test first means specifying expected behavior before the implementation exists.
  • The red-green-refactor cycle proves the test can fail (red) before trusting that it passes (green).
  • Choose the simplest first test case that still forces a real implementation, not a hardcoded return value.
  • Triangulate with a second, different test case to rule out constant-return implementations.
  • Avoid writing test and implementation simultaneously — you lose the verification that the test can catch a bug.
  • Test your own decision logic, not guarantees already provided by the language or framework.
  • Keep the first cycle small and fast; complex edge cases belong in later iterations.

Practice what you learned

Was this page helpful?

Topics covered

#SoftwareTesting#TestingTDDStudyNotes#SoftwareEngineering#WritingYourFirstTestFirst#Writing#Test#Means#Write#Testing#StudyNotes#SkillVeris