SyntaxStudy
Sign Up
Rust Testing, Workspaces, and cargo publish
Rust Beginner 1 min read

Testing, Workspaces, and cargo publish

Rust has first-class support for unit tests, integration tests, and documentation tests built into Cargo. Unit tests live in the same file as the code they test, inside a `#[cfg(test)] mod tests { ... }` block. Integration tests live in a top-level `tests/` directory and test the public API of the crate as an external consumer would. Documentation tests are code examples in doc comments that are compiled and run as tests to ensure examples stay accurate. A Cargo workspace groups multiple crates that are developed together. A root `Cargo.toml` with `[workspace]` and `members = ["crate_a", "crate_b"]` is all that is needed. All workspace members share the same `target/` directory and `Cargo.lock`, enabling cross-crate incremental compilation and ensuring all crates use the same dependency versions. You can run commands for all workspace members at once or target a specific member with `-p crate_name`. Publishing a crate to crates.io requires an account and an API token. Run `cargo login` with your token, then `cargo publish` to upload. Before publishing, `cargo package` creates a `.crate` archive you can inspect; `cargo publish --dry-run` validates everything without actually uploading. A published version is immutable — you can yank a version to prevent new users from depending on it, but existing users are unaffected.
Example
// src/lib.rs — library with unit tests and doc tests

/// Adds two numbers together.
///
/// # Examples
///
/// ```
/// assert_eq!(my_lib::add(2, 3), 5);
/// ```
pub fn add(a: i32, b: i32) -> i32 {
    a + b
}

pub fn divide(a: f64, b: f64) -> Option<f64> {
    if b == 0.0 { None } else { Some(a / b) }
}

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

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

    #[test]
    fn test_add_negative() {
        assert_eq!(add(-1, -2), -3);
    }

    #[test]
    fn test_divide_normal() {
        assert!((divide(10.0, 4.0).unwrap() - 2.5).abs() < 1e-10);
    }

    #[test]
    fn test_divide_by_zero() {
        assert_eq!(divide(1.0, 0.0), None);
    }

    #[test]
    #[should_panic(expected = "intentional")]
    fn test_panic() {
        panic!("intentional panic for demonstration");
    }
}

// tests/integration_test.rs  (separate file in tests/ directory)
// use my_lib;
// #[test]
// fn integration_add() {
//     assert_eq!(my_lib::add(10, 20), 30);
// }

// Run all tests:              cargo test
// Run a specific test:        cargo test test_add_positive
// Run doc tests only:         cargo test --doc
// Run integration tests only: cargo test --test integration_test

This is the last lesson in this section.

Create a free account to earn a certificate