Introduction to TDD with Rust: What, Why, and How

Introduction to TDD

Welcome to the first lesson of our course on Test Driven Development (TDD) using Rust. TDD is an iterative software development process where tests are written prior to developing the actual functionality. This method encourages developers to understand the desired outcomes first, resulting in more reliable and maintainable code.

In this lesson, you'll be introduced to the essential elements of TDD, including the Red-Green-Refactor cycle, which forms the core of this methodology. We'll utilize Rust's built-in testing framework, which provides a robust and well-integrated suite for defining and conducting tests. Let's embark on exploring the core components of TDD through a hands-on example.

Writing the First Test (Red)

The TDD process begins by writing a test that initially fails, representing the "Red" phase. This step helps clarify what the code should accomplish before implementation. Let's write a test for a func function that should ultimately add two numbers.

We can define a test script as follows:

#[cfg(test)]
mod tests {
    use project::*;
    
    #[test]
    fn test_sum() {
        assert_eq!(func(2, 3), 5);
    }
}

Note: We're intentionally using a generic function name func here to demonstrate the TDD process. We'll improve this naming during the refactor phase later in the lesson.

This test script:

  • Defines a test function using Rust's built-in #[test] attribute.
  • Calls the func function and asserts that the result should equal 5.

At this point, we have not yet implemented or even declared the func function. If you try to run cargo test now, Rust will produce a compilation error because func is undefined. This is expected in the purest TDD approach: you write the test first, even if it does not compile.

Expected output:

error[E0425]: cannot find function `func` in this scope
 --> src/lib.rs:7:20
  |
5 |         assert_eq!(func(2, 3), 5);
  |                    ^^^^ not found in this scope

This failure confirms that our test is driving the need for new code. The next step is to declare the func function so the code compiles, and then proceed through the rest of the TDD cycle.

Making the Test Pass (Green)

Our next aim is to write the simplest code possible to make the test pass — the "Green" phase. In TDD, this means implementing minimal functionality to satisfy the test conditions. Let's redefine the func function in the same file:

pub fn func(a: i32, b: i32) -> i32 {
    5
}

This version seems superficial since it doesn't actually add two numbers, but it highlights the TDD focus on passing the test with minimal implementation. By doing so, we've met the test condition. Re-run the test with cargo test, and you'll see:

running 1 test
test tests::test_sum ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

Passing tests validate that our code satisfies the test scenario. Additional test cases will drive the refinement of the implementation.

Sign up

Join the 1M+ learners on CodeSignal

Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal