liveBook · Manning

Test-driven development

As explained in Learn Rust in a Month of Lunches

Test-driven development is a software development technique in which tests are written before implementation code and guide the implementation toward passing behavior.

Test-driven development (TDD) means deciding what code should do by writing tests before implementing it. The tests fail at first. You then write code to make them pass, one by one. It is a choice, not the only way to test, and it is especially useful when you know—or are still working out—what the finished behavior should be. section 14.3 section 14.4

A practical cycle is:

  • Write tests for the behavior you expect. Give each test a descriptive name so a failure is easy to recognize. section 14.3.1
  • Run them before the implementation works. A minimal function or method can let the tests run even while it returns the wrong answer. section 14.3.1
  • Implement behavior, rerun the tests, and use failures to choose the next correction. Add tests when you think of more cases. section 14.3.2

Start with a subtraction test

Suppose a Rust method named math takes the string "1 - 2" and should return -1. A test can state that expectation with assert_eq!(calc.math("1 - 2"), -1). For now, math can simply return 6: the test runs and fails, showing the result that needs to change. Other tests can specify consecutive minus signs, trailing spaces and minus signs, and an input such as "7 - seven" that should panic. section 14.3.1

struct Subtractor;
 
impl Subtractor {
    fn math(&mut self, input: &str) -> i32 {
        6
    }
}
 
#[test]
fn one_minus_two_is_minus_one() {
    let mut calc = Subtractor;               #1
    assert_eq!(calc.math("1 - 2"), -1);
}
#[test]
fn one_minus_minus_one_is_two() {
    let mut calc = Subtractor;
    assert_eq!(calc.math("1 - -1"), 2);
}
#[test]
fn three_minus_three_minus_three_minus_minus_three_is_zero() {
    let mut calc = Subtractor;
    assert_eq!(calc.math("3-3-3--3"), 0);
}
#[test]
fn eighteen_minus_nine_minus_nine_is_zero_even_with_characters_on_the_end() {
    let mut calc = Subtractor;
    assert_eq!(calc.math("18  - 9     -9-----"), 0);
}
#[test]
#[should_panic]                              #2
fn panics_when_characters_not_right() {
    let mut calc = Subtractor;
    calc.math("7 - seven");
}

The initial Subtractor listing keeps the implementation deliberately incomplete while five named tests spell out the behavior to build. section 14.3.1

The first improvement checks for invalid characters. That makes the expected-panic test pass, while the arithmetic tests still fail. Later failures reveal other gaps, including how to handle consecutive minus signs and a number left to process at the end of the input. Each result gives a smaller problem to fix. section 14.3.1 section 14.3.2

Passing tests remain useful when code changes: rerunning them can reveal behavior that has broken. They do not prove the code is perfect, because they check only the cases someone thought to write. Once the tests pass, refactoring is possible; changing the calculator to return a Result instead of panicking is left for another time. section 14.3.2

How Learn Rust in a Month of Lunches covers it

How deep it goes
Works through failing tests and successive corrections, but defers refactoring and returning a Result instead of panicking.
Examples
Uses a string-input subtraction calculator with five tests for arithmetic, consecutive minus signs, trailing characters, and invalid input.
What it assumes
Assumes familiarity with basic Rust functions and methods while introducing the test-first process from its initial tests.
What you can do after
Practice/perform it by writing failing behavior tests, implementing corrections, and rerunning tests after changes.
How it connects
Connects test functions, assertions, expected panics, compiler feedback, and refactoring to the test-first cycle.

Also explained in

Compare how 15 books explain test-driven development →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Learn Rust in a Month of Lunches. The section links point to the parts of the book it draws on.