🚀
12. Testing
Languages

Testing

- create a new library project named adder.

Mar 202510 min read

Testing

Execution Output
$ cargo new adder --lib
  • create a new library project named adder.
      • lib.rs
    • Cargo.lock
    • Cargo.toml
    • lib.rs is the crate root of the library crate.
    pub fn add(left: usize, right: usize) -> usize {
        left + right
    }
     
    #[cfg(test)]
    mod tests {
        use super::*;
     
        #[test]
        fn it_works() {
            let result = add(2, 2);
            assert_eq!(result, 4);
        }
    }
    1. assert! macro is used to check if the result of the function is true.
    2. assert_eq! macro is used to compare the result of the function with the expected value.
    3. assert_ne! macro is used to check if the two values are not equal.
    • #[cfg(test)] attribute is used to compile the test code only when running tests.
    • #[test] attribute is used to define a test function.
    Execution Output
    $ cargo test
    
    running 1 test
    test tests::it_works ... ok
    
    test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
    • A test fails when something inside the test function panics.
    • Each test function runs in a new thread, if the main thread sees that the new thread has panicked, it reports the test as failed.
    pub fn add(left: usize, right: usize) -> usize {
        left + right
    }
     
    #[cfg(test)]
    mod tests {
        use super::*;
     
        #[test]
        fn it_works() {
            let result = add(2, 2);
            assert_eq!(result, 4);
        }
     
        #[test]
        fn failing_test() {
            panic!("Test failed");
        }
    }
    Execution Output
    $ cargo test
    
    running 2 tests
    test tests::it_works ... ok
    test tests::failing_test ... FAILED
    struct Rectangle {
        width: i32,
        height: i32,
    }
     
    impl Rectangle {
        fn can_hold(&self, other: &Rectangle) -> bool {
            self.width > other.width && self.height > other.height
        }
    }
     
    #[cfg(test)]
    mod tests {
     
        use super::*;
     
        #[test]
        fn it_works() {
            let rect = Rectangle {
                width: 5,
                height: 4,
            };
     
            let small_rect = Rectangle {
                width: 4,
                height: 3,
            };
     
            assert!(rect.can_hold(&small_rect));
        }
    }
    Execution Output
    $ cargo test
    
    running 1 test
    test tests::it_works ... ok
    
    • use super::* brings the Rectangle struct into tests module scope.

    Custom Failure Messages

    pub fn add(left: usize, right: usize) -> usize {
        left + right
    }
     
    #[cfg(test)]
    mod tests {
        use super::*;
     
        #[test]
        fn add_working() {
            let result = add(2, 2);
            assert_eq!(result, 5, "2 + 2 should be 5, not {}", result);
        }
    }
    Execution Output
    $ cargo test
    
    running 1 test
    test tests::add_working ... FAILED
    

    Asserting that a Function Panics

    #[cfg(test)]
    mod tests {
        use super::*;
     
        #[test]
        #[should_panic]
        fn add_working() {
            let result = add(2, 2);
            assert_eq!(result, 5, "2 + 2 should be 5, not {}", result);
        }
    }
    Execution Output
    $ cargo test
    
    running 2 tests
    test tests::it_works ... ok
    test tests::add_working - should panic ... ok
    
    test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
    • #[should_panic] attribute is used to check if the test function panics.
    pub struct Guess {
        value: i32,
    }
     
    impl Guess {
        pub fn new(value: i32) -> Guess {
            if value < 1 {
                panic!("Guess Value must be greater than 0, got {}", value);
            } else if value > 100 {
                panic!("Guess Value must be less than 101, got {}", value);
            }
     
            Guess { value }
        }
    }
     
    #[cfg(test)]
    mod tests {
        use super::*;
     
        #[test]
        #[should_panic(expected = "Guess Value must be less than 101")]
        fn valid_guess() {
            Guess::new(101);
        }
     
        #[test]
        #[should_panic(expected = "Guess Value must be less than 101")]
        fn valid_guess2() {
            Guess::new(0);
        }
    }
    Execution Output
    $ cargo test
    
    running 2 tests
    test tests::valid_guess - should panic ... ok
    test tests::valid_guess2 - should panic ... FAILED
    • #[should_panic(expected = "message")] attribute is used to check if the test function panics with the expected message.

    Returning Result Types

    #[cfg(test)]
    mod tests {
        #[test]
        fn it_works() -> Result<(), String> {
            if 2 + 3 == 4 {
                Ok(())
            } else {
                Err(String::from("2 plus 2 does not equal four"))
            }
        }
    }
     
    Execution Output
    $ cargo test
    
    running 1 test
    test tests::it_works ... FAILED
    

    Showing Output

    fn prints_and_return_10(a: i32) -> i32 {
        println!("I got the value {}", a);
     
        10
    }
     
    #[cfg(test)]
    mod tests {
        use super::*;
     
        #[test]
        fn this_test_will_pass() {
            let value = prints_and_return_10(4);
     
            assert_eq!(10, value);
        }
     
        #[test]
        fn this_test_will_fail() {
            let value = prints_and_return_10(8);
            assert_eq!(5, value)
        }
    }
    $ cargo test -- --show-output
    
    running 2 tests
    test tests::this_test_will_pass ... ok
    test tests::this_test_will_fail ... FAILED
    
    failures:
    
    ---- tests::this_test_will_fail stdout ----
    I got the value 8
    thread 'tests::this_test_will_fail' panicked at src\lib.rs:131:9:
    assertion `left == right` failed
      left: 5
     right: 10
    note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

    Running specific tests

    Execution Output
    $ cargo test this_test_will_pass
    
    running 1 test
    test tests::this_test_will_pass ... ok
    
    test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 1 filtered out; finished in 0.00s
    • cargo test test_name is used to run a specific test, which starts with the name test_name.

    Ignoring Tests

    #[cfg(test)]
    mod tests {
        use super::*;
     
        #[test]
        fn it_works() {
            assert_eq!(2 + 2, 4);
        }
     
        #[test]
        #[ignore]
        fn expensive_test() {
            // code that takes an hour to run
        }
    }
    Execution Output
    $ cargo test
    
    running 1 test
    test tests::it_works ... ok
    
    test result: ok. 1 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 0.00s
    • #[ignore] attribute is used to ignore the test function.

    Running Ignored Tests

    Execution Output
    $ cargo test -- --ignored
    
    running 1 test
    test tests::expensive_test ... ignored
    
    test result: ok. 0 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 0.00s

    Test Organization

    • Unit tests are small and more focused, test one module in isolation and test private interfaces.
    • Integration tests are larger and test the entire program, test the public interfaces that the library provides to other libraries.
      • integration_test.rs
  • use adder;
     
    fn it_adds_two(){
        assert_eq!(adder::add(2, 2), 4);
    }
    Execution Output
    $ cargo test
    
    running 1 test
    test tests::internal ... ok
    
    Execution Output
    $ cargo test --test integration_test
    
    running 1 test
    test it_adds_two ... ok
    
    test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
    • cargo test --test test_name is used to run a specific integration test.

    • tests directory treats each file as a separate crate, (this leads unexpected behavior when using use super::*).

    • multiple integration test files and you want to share common code, you can put the common code into a tests/common.rs File

      • common.rs
      • integration_test.rs
  • pub fn setup() {
        // setup code
    }
     
    Execution Output
    $ cargo test
    
    running 1 test
    test tests::internal ... ok
    
    • Here common.rs is treated like a integration test file(not intended).

    To avoid this issue

        • mod.rs
      • integration_test.rs
  • pub fn setup() {
        // setup code
    }
     
    Execution Output
    $ cargo test
    
    running 1 test
    test tests::internal ... ok
    
    • common directory is treated as a module not as test crate, so it doesn't run as a test.
    • common/mod.rs allows to use the common code in the integration test file.
      • integration_test.rs
  • use adder;
     
    mod common;
     
    #[test]
    fn it_adds_two() {
        common::setup();
     
        assert_eq!(4, adder::add_two(2))
     
    }
     
    • this way the common code can be shared between multiple integration test files.
    ⚠️
    • lib.rs creates a library crate.
    • main.rs creates an binary crate.

    Cannot test binary crates with the integration tests.

    © 2026 Driptanil Datta. All rights reserved.

    Software Developer & Engineer

    Disclaimer:The content provided on this blog is for educational and informational purposes only. While I strive for accuracy, all information is provided "as is" without any warranties of completeness, reliability, or accuracy. Any action you take upon the information found on this website is strictly at your own risk.

    Copyright & IP:Certain technical content, interview questions, and datasets are curated from external educational sources to provide a centralized learning resource. Respect for original authorship is maintained; no copyright infringement is intended. All trademarks, logos, and brand names are the property of their respective owners.

    System Operational

    Built with Love ❤️ | Last updated: Mar 16 2026