Testing in Rust: unit, integration, e2e — complete guide

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Introduction to Testing in Rust



Rust is a systems programming language known for its safety and performance. However, even the most reliable code needs verification. Testing in Rust is not just an option, but an integral part of the development process. Thanks to built-in test support in Cargo, you can easily write and run unit, integration, and e2e tests. In this article, we will cover all three levels of testing with practical examples.



Unit Tests in Rust: The Foundation of Reliability



Unit tests check individual functions and methods in isolation. In Rust, they are typically placed directly in the code file, in a special tests module.



Structure of a Unit Test



Each unit test is a function annotated with the #[test] attribute. Inside it, you use the assert!, assert_eq!, and assert_ne! macros to verify results.



// src/math.rspub fn add(a: i32, b: i32) -> i32 {    a + b}

pub fn divide(a: f64, b: f64) -> Result<f64, String> { if b == 0.0 { Err(String::from("Division by zero")) } else { Ok(a / b) }}

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

#[test] fn test_add() { assert_eq!(add(2, 3), 5); }

#[test] fn test_divide_success() { let result = divide(10.0, 2.0); assert!(result.is_ok()); assert_eq!(result.unwrap(), 5.0); }

#[test] fn test_divide_by_zero() { let result = divide(10.0, 0.0); assert!(result.is_err()); assert_eq!(result.unwrap_err(), "Division by zero"); }}


Note the #[cfg(test)] attribute. It tells the compiler to include this module only when building tests. This saves compilation time for the final binary.



Running Unit Tests



To run all tests in the project, use the command:



cargo test


You can also run a specific test by name:



cargo test test_add


Or filter tests by a keyword:



cargo test divide


Integration Tests in Rust: Checking Interaction



Integration tests check how different modules of your application work together. Unlike unit tests, they are located in a separate tests/ folder at the root level of the project.



Structure of an Integration Test



Create a file tests/integration_test.rs:



// tests/integration_test.rsuse my_project::math; // assuming the library is called my_project

#[test]fn test_add_and_divide() { let sum = math::add(10, 5); let result = math::divide(sum as f64, 3.0).unwrap(); assert_eq!(result, 5.0);}

#[test]fn test_complex_calculation() { let a = math::add(100, 200); let b = math::add(50, 50); let result = math::divide(a as f64, b as f64).unwrap(); assert_eq!(result, 3.0);}


Each file in tests/ is compiled as a separate crate. This means you can have multiple test files for different scenarios.



Common Module for Integration Tests



If you need shared utilities for all integration tests, create a subfolder tests/common/ with a mod.rs file:



// tests/common/mod.rspub fn setup() {    // Initializing the test environment    println!("Setting up test environment...");}


And use it in tests:



// tests/integration_test.rsmod common;

#[test]fn test_with_setup() { common::setup(); // Your test}


E2E Tests in Rust: Full Immersion



E2E (end-to-end) tests check the operation of the entire application from start to finish. In Rust, this is often done using ex

Blogs

Book Recommendations