Skip to main content
Rust Programming
CHAPTER 27 Beginner

Testing in Rust

Updated: May 18, 2026
5 min read

# CHAPTER 27

Testing in Rust

1. Chapter Introduction

In many languages, setting up a testing framework (like Jest or JUnit) takes hours of configuration. Rust believes testing is so critical that it built a world-class test runner directly into the language and the Cargo toolchain. You don't need to install *anything* to start testing. In this chapter, we will learn how to write Unit Tests, assert logic, and organize Integration Tests to guarantee our software works flawlessly.

2. Learning Objectives

By the end of this chapter, you will be able to:
  • Write Unit Tests using the #[test] attribute.
  • Use assert!, asserteq!, and assertne! macros.
  • Test for expected Panics using #[should_panic].
  • Organize tests using the cfg(test) module.
  • Write Integration Tests in the tests/ directory.

3. Writing Your First Unit Test

Unit tests verify that a small, isolated piece of code (like a single function) works correctly. To mark a function as a test, simply add the #[test] attribute above it.
rust
123456789101112131415161718
// The function we want to test
pub fn add_two(a: i32) -> i32 {
    a + 2
}

// Testing module
#[cfg(test)]
mod tests {
    // Import everything from the outer scope
    use super::*; 

    #[test]
    fn it_adds_two_correctly() {
        let result = add_two(2);
        // Assert that the result equals 4
        assert_eq!(result, 4); 
    }
}

4. Running Tests

To execute your tests, open your terminal and simply type:
bash
1
cargo test

Cargo compiles your code in a special test mode, runs all functions marked with #[test], and prints a beautiful green ok or a red FAILED.

5. Assertion Macros

Rust provides three primary macros to verify outcomes:
  1. 1. assert!(expression): Passes if the boolean expression evaluates to true.
  1. 2. asserteq!(left, right): Passes if both sides are equal. Prints both values if they fail, which makes debugging very easy!
  1. 3. assertne!(left, right): Passes if both sides are NOT equal.
rust
12345678910111213
#[test]
fn test_greeting() {
    let result = String::from("Hello Carol");
    
    // Checks if the string contains the name
    assert!(result.contains("Carol"));
    
    // You can add custom failure messages!
    assert!(
        result.contains("Carol"),
        "Greeting did not contain name, value was `{}`", result
    );
}

6. Testing for Panics

Sometimes, a function *should* crash if given bad data. We can test that a panic correctly occurs using the #[should_panic] attribute.
rust
12345678910111213141516
pub fn guess_number(value: i32) {
    if value < 1 || value > 100 {
        panic!("Guess value must be between 1 and 100!");
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    #[should_panic(expected = "between 1 and 100")] // We can even check the panic message!
    fn greater_than_100_should_panic() {
        guess_number(200); // This will panic, which means the test PASSES!
    }
}

7. Unit Tests vs. Integration Tests

Unit Tests:
  • Written in the *same file* as the code they are testing.
  • Wrapped in a #[cfg(test)] module so they are not compiled into your final production binary.
  • Can test private functions.

Integration Tests:

  • Verify that different parts of your library work together correctly from the outside.
  • Placed in a separate folder at the root of your project called tests/.
  • Can ONLY test pub (public) functions.

Creating an Integration Test:

  1. 1. Create a folder tests/ next to src/.
  1. 2. Create a file tests/integration_test.rs.

rust
12345678
// In tests/integration_test.rs
// Bring your library into scope (assuming your package is named 'my_app')
use my_app;

#[test]
fn test_public_api() {
    assert_eq!(my_app::add_two(2), 4);
}

8. Common Mistakes

  • Forgetting use super::*;: Inside the mod tests block, you must import the functions you want to test from the parent scope. If you don't, the compiler will say the function is not found.
  • Testing Private Functions in tests/: Integration tests act exactly like external users. If a function is not marked pub, your integration tests cannot see it.

9. Best Practices

  • Test Driven Development (TDD): Rust's incredibly fast test runner makes it perfect for TDD. Write the #[test] first, watch it fail, then write the code to make it pass.

10. Exercises

  1. 1. Write a function iseven(num: i32) -> bool.
  1. 2. Create a test module.
  1. 3. Write one test testeven using assert!(iseven(4)).
  1. 4. Write one test testodd using asserteq!(iseven(5), false).
  1. 5. Run cargo test.

11. MCQs with Answers

Q1. Do you need to install a third-party crate like Jest or JUnit to test Rust code? a) Yes b) No, a world-class test runner is built into Rust and Cargo natively. Answer: b) No, it is built-in.

Question 2

What terminal command executes all tests in your project?

Question 3

What attribute marks a standard function as a test?

Question 4

What does the asserteq!(a, b) macro do?

Question 5

Why is asserteq! often preferred over assert!(a == b)?

Question 6

What does the #[cfg(test)] attribute do?

Question 7

If a function is supposed to crash (panic) when given invalid input, how do you verify this in a test?

Question 8

Where do Unit Tests live?

Question 9

Where do Integration Tests live?

Q10. Can Integration Tests access private functions (fn without pub)? a) Yes b) No, they act as external consumers of your library and can only access public APIs. Answer: b) No, they can only access public APIs.

12. Interview Questions

  • Q: Differentiate between Unit Tests and Integration Tests in the Rust ecosystem.
  • Q: Explain how conditional compilation (#[cfg(test)]) benefits the final production build of a Rust application.

13. Summary

Software engineering is only as good as its tests. Rust elevates testing to a first-class citizen, integrating macros and runners directly into the toolchain. By utilizing unit tests for internal logic and integration tests for public API verification, you can guarantee that the memory safety provided by the compiler is matched by the logical correctness of your business rules.

14. Next Chapter Recommendation

You now possess the technical skills required to build production-grade Rust software. The next step is landing the job. In Chapter 28: Rust Interview Preparation, we will cover the top 50 interview questions, whiteboard coding challenges, and the core concepts employers look for in Rust developers.

Finish this Chapter

Save your progress on your learning path and prepare for coding interview challenges.

Discussion

Join the discussion

Log in or create a free account to participate.

Sort: ·