The Birth of xUnit — From SUnit to JUnit, the Legendary In-Flight Pair Programming

By 1979, Myers had systematized testing as theory (Episode 1). But there was still no everyday tool for running tests automatically. Kent Beck's small framework SUnit, built for Smalltalk, and the story of JUnit supposedly born from pair programming with Erich Gamma on a flight. This traces how the vocabulary of TestCase, assert, and fixture became the world standard for unit testing.

testinghistoryjunitsunitkent-beckunit-testingxunit

The Theory Existed. The Tools Didn’t

As we saw in the previous episode, by the end of the 1970s, software testing had been systematized in theory. Myers had gathered testing’s purpose and techniques into a single book, Dijkstra had exposed its limits, and Boehm had distinguished verification from validation. In practice, though, no common tooling existed for writing, running, and checking the results of unit tests.

In many shops, testing meant eyeballing output from print statements, or writing throwaway scripts to check things once and discard them — a personal, low-reproducibility kind of work. Repeating the same check took effort every time, and the very idea of “running tests automatically, over and over” wasn’t yet common.

What changed this entirely was a small testing framework Kent Beck built for Smalltalk, called SUnit, and JUnit, the framework he ported it to in Java. This article traces how these two frameworks came to be, and how the “xUnit architecture” they established transformed what unit testing looked like.

Kent Beck and SUnit — an Experiment in Unit Testing for Smalltalk

In the late 1980s, Kent Beck wrote a small testing framework in the Smalltalk environment to support his own programming. This is what later came to be called SUnit.

Beck documented this design in 1994 as a set of patterns, in a paper titled “Simple Smalltalk Testing: With Patterns,” published in The Smalltalk Report. Rather than merely showing working code, the paper articulated the design decisions — why this particular structure — in the form of patterns, and it had a major influence on the design philosophy of later xUnit-family frameworks. On the exact birth year of SUnit: the published paper itself dates to 1994, but Beck himself has said in later years that he “thinks” he wrote it in 1994, while some accounts suggest the initial idea goes back further still — the record shows some wobble depending on the source.

SUnit’s design is remarkably simple.

  • A test is written as a subclass of TestCase
  • Each test method corresponds to one thing being checked
  • setUp prepares each test’s preconditions (the fixture)
  • A message like assert: compares the expected value against the actual value
  • Multiple test cases are gathered into a TestSuite and run together

The key point is that Beck built this as a “framework.” That is, he made running tests, tallying results, and reporting failures — the repetitive work — the framework’s responsibility, not the responsibility of the person writing the test. This idea became the practical foundation for Extreme Programming (XP) and Test-Driven Development (TDD), both of which Beck would go on to advocate. We cover TDD’s detailed history in Episode 4.

The Legendary Flight — How JUnit Was Born

In 1997, Kent Beck happened to be on a flight from Zurich to Atlanta for the OOPSLA ’97 conference, seated alongside Erich Gamma, the software engineer also known as a co-author of Design Patterns (1994).

Over the course of that long flight, the two of them are widely said to have ported the ideas behind SUnit to Java through pair programming — and, moreover, test-first. The code written during that in-flight session became the prototype for the first version of JUnit.

JUnit was released shortly afterward and quickly became the standard unit testing tool in the Java community. Because it combined the xUnit-style design philosophy Beck had already established in the Smalltalk community with Gamma’s deep expertise in object-oriented design, JUnit ended up as more than just “a port of SUnit” — it became a highly polished design that every subsequent language’s testing framework would look to as a model.

This anecdote is often retold in testing history not merely as a charming story. It’s emblematic because the practice of “building the test framework itself in a test-driven way” — which Beck would later systematize in Test-Driven Development: By Example (2002) — was already embodied in the very scene where JUnit was born.

The Components of the xUnit Architecture

The basic structure that SUnit and JUnit established, and that nearly every subsequent unit testing framework would go on to follow, is called the “xUnit architecture.” Its components are as follows.

Component Role
TestCase A class or function representing a single thing being checked (a test case). Usually the smallest independently runnable unit
TestSuite A container for gathering multiple TestCases to run them together
TestRunner The program that actually runs a TestSuite and collects and reports successes, failures, and errors
Assertion A mechanism that checks something like “does the expected value match the actual value,” failing the test if not (e.g., assertEquals)
Fixture The preconditions and environment a test needs to run (the initial state of the object under test, test data, and so on)
setUp / tearDown Hooks that prepare a fixture before each test runs (setUp) and clean up afterward (tearDown)

The heart of this structure lies in the principle of “preparing an independent fixture fresh for each test case.” Avoiding interdependence — where one test fails because another test failed to clean up after itself — is what keeps testing reliable and reproducible. This design decision would later become an essential principle for preventing flaky tests (see Episode 14). Test independence is an unglamorous-sounding principle, but it’s one of the reasons the xUnit architecture hasn’t aged in decades.

Why Frameworks Made Unit Testing General and Automatic

Even before SUnit and JUnit, developers checked their work’s behavior in one way or another. But it was personal work with the following characteristics.

  • No shared shape to how test code was written; each team or project did it differently
  • Tallying and reporting results was manual, making the cost of running many test cases high
  • Test independence — not being affected by a previous test — wasn’t guaranteed, so reliability was low
  • “Writing tests” wasn’t recognized as a standard part of a programmer’s job

The arrival of the xUnit architecture changed this situation as follows.

  1. A shared shape emerged — sharing the vocabulary of TestCase, assert, and setUp dramatically lowered the cost of reading and writing tests someone else wrote
  2. Running and tallying got automated — a TestRunner would run all the tests together and show, in an instant, whether things were green (all passing) or red (some failing)
  3. Integration with IDEs and build tools advanced — JUnit became deeply integrated with IDEs (like Eclipse) and build tools (like Ant and Maven), normalizing the developer experience of “tests run every time you save”
  4. The cost of writing a test dropped dramatically — you only needed to add a test method following a fixed shape, which lowered both the psychological and time cost of writing tests in the first place

As a result, unit testing shifted from a practice of a handful of especially conscientious developers into an ordinary, industry-wide part of the job. This was the moment the thread of “from manual to automated,” mentioned in the series overview, took its first major leap forward.

A Simple JUnit-Style Code Example

To get a feel for the xUnit architecture, here’s a JUnit-style test example for a simple calculator class, written in the modern annotation style of JUnit 5 (Jupiter).

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.BeforeEach;
import static org.junit.jupiter.api.Assertions.assertEquals;

class CalculatorTest {

    private Calculator calculator;

    @BeforeEach
    void setUp() {
        // Prepare a fresh instance before each test (the fixture)
        calculator = new Calculator();
    }

    @Test
    void addsTwoPositiveNumbers() {
        int result = calculator.add(2, 3);
        assertEquals(5, result); // assertion
    }

    @Test
    void addsNegativeNumbers() {
        int result = calculator.add(-2, -3);
        assertEquals(-5, result);
    }
}

@BeforeEach (the modern name for setUp) handles preparing the fixture, each @Test method runs as an independent TestCase, and assertEquals plays the role of the assertion. This structure has barely changed since the SUnit and JUnit of the 1990s, which speaks to just how long the xUnit architecture’s lifespan has been.

JUnit’s Later Evolution, and Its Enormous Influence

JUnit itself kept evolving after this. It went through generations: the classic TestCase-inheritance style of JUnit 3; JUnit 4, which introduced the annotation-based @Test style in 2006; and JUnit 5 (Jupiter), overhauled into a modular architecture in 2017.

JUnit went on to exert wide influence well beyond the Java community. As unit testing shifted from “a habit of a few enthusiastic developers” into “an industry-standard practice,” JUnit remained the central catalyst throughout that process.

  • The design — building a class around the unit of a TestCase, verifying with assert, and managing fixtures with setUp/tearDown — was ported to other languages almost exactly as-is. We cover this wave of porting in the next episode, Episode 3
  • JUnit’s success cemented, across the industry, the idea that test code, too, is a first-class deliverable worth maintaining
  • The practice of “building the test framework itself in a test-driven way” also anticipated the thinking that would later be systematized as TDD

Conclusion — A Vocabulary That Changed the World

The births of SUnit and JUnit brought about a shift: writing tests stopped being a special task and became part of the ordinary work of programming. The core of this shift lies in establishing a shared vocabulary and architecture — TestCase, assertion, fixture, setUp/tearDown.

This design turned out to be extraordinarily universal. As we’ll see in the next article, it was ported, largely unchanged in spirit, to nearly every major language — C++, .NET, Python, Ruby, PHP, and more. A small piece of code written during one flight has gone on to shape the daily work of developers around the world for nearly thirty years — which is itself one of the more surprising turns in the history of testing.

The next episode, Episode 3, looks at how JUnit’s design philosophy spread into each language’s community, and how it transformed to fit each language’s own culture along the way.

← Back to The Lineage of Software Testing