The Lineage of Software Testing — 70 Years of Questioning 'Correctness,' from Debugging to the AI Era
From the 1940s tale of the 'first bug,' through the birth of xUnit, the rise of TDD and BDD, mocks and property-based testing, the generational turnover of E2E tools, coverage and mutation testing, the fight against flaky tests, and the present moment where generative AI is reshaping how tests get written. This is the map for an 18-part series tracing how the questions 'how do we know a program is correct' and 'what can and can't a test guarantee' have been rebuilt in every era.
Does Software Testing Have a “History”?
It does — seventy-odd years of it. And the crucial point is that software testing is not a single, monolithic “correct way to do it”: it has been rebuilt from the foundations in every era. What a programmer in the 1960s meant by “testing,” what a TDD practitioner in the 2000s meant by it, and what a developer pairing with an AI in the 2020s means by it rest on entirely different premises.
Just as the sister series [The Lineage of Mathematics] and [The Lineage of Music Theory] traced their disciplines as a history of changing questions and answers, this series traces software testing as a history of ideas. The question at its center is simple, and still unresolved.
How do we know a program is correct? And what can a test guarantee — and what can’t it?
The answers to these two questions have been rewritten again and again over seventy years, moving from manual debugging, to automated frameworks, to generative AI. This series follows that history of rewrites.
Four Threads Running Through It
Four oppositions and problems run through these seventy years. Every episode touches one of these four threads.
- Verification vs. validation — “Are we building the product right? (verification)” versus “are we building the right product in the first place? (validation).” Testing has stayed almost entirely in the territory of verification, and validation has remained a separate problem
- From manual, to automated, to AI — who, or what, writes and runs the tests? The agency has shifted from manually checking punch cards, to xUnit frameworks, to automatic runs in CI, to AI-generated first drafts
- Where do you check? — how much weight goes on unit, integration, or E2E tests? The clash of strategy models — the test pyramid, the testing trophy, the test honeycomb, and the ice-cream cone (an anti-pattern)
- The limits of coverage — the fundamental limit posed by Dijkstra’s warning that “testing can show the presence of bugs, but never their absence.” Formal methods are the other path, one that tries to close this gap mathematically
Seventy Years in Five Eras — A Map of All 18 Episodes
Here’s the shorthand version of the timeline before we go further: the details belong to each episode, but this is worth grasping in one line per era first.
| Era | In one line |
|---|---|
| Pre-1970s | An extension of manual checking and debugging. Testing was not yet an independent discipline |
| Mid-1990s | SUnit establishes the archetypal xUnit patterns — fixtures, test runners, and the rest |
| 1997-2002 | JUnit’s spread and the systematization of TDD turn testing into “an act that drives design” |
| Late 2000s | BDD, Cucumber, and xUnit Test Patterns crystallize testing’s vocabulary and techniques |
| Early 2010s | Frontend-oriented frameworks (Jasmine, then Jest) spread explosively |
| Late 2010s | Selenium’s near-monopoly gives way to Cypress and Puppeteer, a generational handover in E2E tools |
| 2020s | Playwright unifies multi-browser automation, while LLM-generated tests take hold |
Below, all 18 episodes (excluding this overview) are grouped into five parts.
Part I: Prehistory and the Dawn of Automation (Episodes 1-3)
From an era when “testing” was not yet an independent discipline, to the moment xUnit became a shared language spoken around the world.
| Episode | Title | Contents |
|---|---|---|
| Episode 1 | Prehistory — From Debugging to Testing | The 1950s, when debugging and testing were not yet distinguished; the software crisis; Dijkstra’s warning; Myers’s The Art of Software Testing (1979) |
| Episode 2 | The Birth of xUnit | Kent Beck’s SUnit (Smalltalk); the story of JUnit supposedly written on an airplane; the establishment of a shared vocabulary — TestCase, assert, fixture |
| Episode 3 | xUnit’s Global Spread | How JUnit’s design philosophy got ported to C++, .NET, Python, Ruby, PHP, and beyond |
Part II: The Rise of an Idea (Episodes 4-7)
The era in which testing’s meaning shifted from “hunting for bugs” to “an act that drives design.”
| Episode | Title | Contents |
|---|---|---|
| Episode 4 | The Rise of TDD, and Its Controversies | Kent Beck’s Test-Driven Development: By Example (2002); red-green-refactor; the “TDD is dead” debate |
| Episode 5 | Onward to BDD | Dan North’s proposal of BDD; Gherkin and Cucumber; the idea of replacing the word “test” with the language of “behavior” |
| Episode 6 | The Story of Mocks | The theorizing of mock objects; the clash between the London school and the Detroit school (classicist) |
| Episode 7 | Property-Based Testing | QuickCheck and Hypothesis — the shift from writing individual examples to writing properties |
Part III: Beyond the Browser Wall (Episodes 8-10)
How testing evolved on the new battleground of browsers and the frontend.
| Episode | Title | Contents |
|---|---|---|
| Episode 8 | The Generational Turnover of E2E | Selenium, then Cypress, then Playwright — successive generations of browser automation tools |
| Episode 9 | The Evolution of JavaScript Testing | Jasmine, then Jest; the development of a testing culture unique to the frontend |
| Episode 10 | Where Language Ecosystems Stand Today | The current standard tools across Java/Kotlin, Python, Ruby, Rust, Go, and more |
Part IV: Design and Technique (Episodes 11-13)
The era that digs into “what makes a good test” as a matter of technique.
| Episode | Title | Contents |
|---|---|---|
| Episode 11 | The Shape of Tests | The test pyramid, testing trophy, and test honeycomb — choosing among strategy models |
| Episode 12 | Unit Test Design and Contracts | Principles for designing good unit tests; reducing integration cost with contract testing |
| Episode 13 | Coverage and Mutation | The theory and limits of coverage metrics; measuring “test strength” with mutation testing |
Part V: Modern Practice and Its Limits (Episodes 14-18)
Where testing is headed amid the modern variables of organization, scale, and AI.
| Episode | Title | Contents |
|---|---|---|
| Episode 14 | The Fight Against Flaky Tests | How unstable tests erode an organization’s trust, and the cycle of detection, quarantine, and elimination |
| Episode 15 | The Limits of Testing and Formal Methods | The relationship to formal verification and model checking, which try to mathematically close the gap left by Dijkstra’s warning |
| Episode 16 | Quality, Everyone’s Job | The historical evolution of the QA profession, and the modern idea that “quality is everyone’s responsibility” |
| Episode 17 | Testing at Scale | The practical challenges of running tests in monorepos and large organizations |
| Episode 18 | Testing in the AI Era | The possibilities and pitfalls of LLM-generated tests, and the new question of how to trust a test an AI wrote |
From “Finding Bugs” to “Making Change Possible”
The very purpose we assign to testing has been repainted again and again over these seventy years. In the late 1970s, Myers defined testing as “the process of executing a program with the intent of finding errors,” framing it as a destructive act. In the 1990s, once Kent Beck systematized TDD, testing’s meaning shifted from “confirming code you already wrote” to “an act that drives the design of the implementation.” From the 2000s onward, as continuous delivery spread, testing outgrew being an individual developer’s safety net and took on the role of “social infrastructure that lets an organization ship changes to production safely.”
Tracing this shift in meaning is worth more than just glancing at a technical timeline. Knowing what each era expected from testing is the shortcut to understanding why today’s practices — TDD, CI, coverage targets, contract testing, and the rest — took the shape they did. Put differently, today’s “obvious” testing habits are themselves just one stopping point on the way to being rebuilt under some different set of assumptions.
What Testing Can Guarantee, and What It Can’t
There’s one more boundary this series keeps returning to. A test passing (green) means only that “no error was found within the range that was tried.” As Dijkstra pointed out, since testing means selecting and running a finite number of cases out of a near-infinite space of possible inputs, it can never, in principle, prove that “not a single bug exists.” Formal methods (Episode 15) are the attempt to close this gap mathematically; modern testing strategy (Episodes 11-13 and 17) is the ongoing effort to accept this limit and judge, in practice, how much risk is tolerable. The history of testing is also a record of how each era has redrawn this boundary between what can be guaranteed and what can’t.
Relationship to the Sister Series
This series shares the same “history of ideas” approach as [The Lineage of Mathematics] and [The Lineage of Music Theory]. Just as mathematics is the history of the practice of “proof,” and music theory is the history of “how we conceive of consonance and dissonance,” software testing is the history of the question “how do we confirm a program is correct.” What the three series share is a point of view: today’s way of doing things was never inevitable from the start — it is merely one waypoint reached after argument and trial and error.
Who This Series Is For
This series has the following readers in mind.
- Anyone who has heard terms like TDD, BDD, mocks, and property-based testing, but doesn’t know where they came from or what disputes shaped them
- Practitioners who want to revisit their team’s testing strategy — pyramid shape, coverage targets, flaky-test countermeasures — with historical context in hand
- Anyone who wants to understand why today’s testing practices take the shape they do, not as a pile of disconnected tips, but as an accumulation of history
How to Read from Here
Read straight through from Episode 1 and you can follow one continuous thread, from the undifferentiated “debugging” of the 1950s to AI-assisted testing in the 2020s. If you’re drawn to a specific technique — mocks, property-based testing, coverage — feel free to jump straight to that episode. Rather than dates and names for their own sake, this series cares more about the chain of causation: why an idea arose when it did, what it clashed with, and what it left behind.
The next episode, Episode 1, begins the story in an era when the word “testing” didn’t yet carry an independent meaning of its own — the 1950s, when debugging and testing were still one and the same thing.