rust-tests-unitarios-assert

Unit Testing in Rust: assert! and basic cargo test

  • 4 min

The :idea[testing] in Rust brings together ***tools to automatically verify code***.

In other languages, to do testing you have to choose a framework (JUnit, PyTest, Mocha...), install it, configure it, and learn its specific syntax.

In :idea[Rust], testing comes "built-in".
The philosophy is simple: If you write a function, you should be able to test it immediately in the same file.

## The anatomy of a test

A test in Rust is simply a function annotated with the **`#[test]`** attribute.
When you run `cargo test`, Rust finds all functions with this tag, compiles them into a separate test binary, and executes them.

```rust
// src/lib.rs

fn sumar(a: i32, b: i32) -> i32 {
    a + b
}

#[cfg(test)]
mod tests {
    use super::*; // We import everything from the parent module

    #[test]
    fn prueba_suma_simple() {
        let resultado = sumar(2, 2);
        assert_eq!(resultado, 4);
    }
}
Copied!

Running the tests

Open your terminal and type:

$ cargo test
Copied!

You will see detailed output indicating which tests passed (ok) and which failed (FAILED).

Assertion macros

Inside a test, we check that the result is the expected one using macros. If the condition is not met, the macro triggers a panic!, which Rust interprets as “test failed”.

assert!(condition)

Verifies that a boolean value is true.

#[test]
fn prueba_booleana() {
    let es_mayor = 10 > 5;
    assert!(es_mayor); // Passes if true, fails if false
}
Copied!

assert_eq!(a, b) and assert_ne!(a, b)

Check equality (equal) or inequality (not equal). They are better than assert!(a == b) because if they fail, they show you both values (the expected and the actual), which makes debugging much easier.

#[test]
fn prueba_igualdad() {
    let resultado = 2 + 2;
    assert_eq!(resultado, 4); // Compares both sides and the test passes
}
Copied!

Custom messages

All these macros accept extra arguments to print a custom message if they fail.

let resultado = sumar(2, 3);
assert_eq!(resultado, 5, "Sum function is broken with inputs 2 and 3");
Copied!

Tests that should fail: #[should_panic]

Sometimes you want to make sure your code reacts correctly to errors, that is, it panics when it should (for example, when validating invalid arguments).

For this we use the #[should_panic] attribute.

pub fn dividir(a: i32, b: i32) -> i32 {
    if b == 0 {
        panic!("Thou shalt not divide by zero");
    }
    a / b
}

#[test]
#[should_panic(expected = "Thou shalt not divide by zero")] // Optional: verify message
fn prueba_division_cero() {
    dividir(10, 0); // This must crash for the test to pass
}
Copied!

If the dividir function does NOT panic, then the test will fail (because we expected a panic and it didn’t happen).

Organizing unit tests

The convention in Rust is to write unit tests in the same file as the code they test.

To avoid cluttering the compiled source code, they are usually placed inside a submodule called tests annotated with #[cfg(test)].

// src/main.rs or src/lib.rs

// --- ACTUAL CODE ---
fn funcion_privada() -> i32 { 5 }

// --- TESTS ---
#[cfg(test)] // Only compile this module when running 'cargo test'
mod tests {
    use super::*; // Brings functions from the parent into scope

    #[test]
    fn test_internas() {
        // Being in the same file, we can test private functions!
        assert_eq!(funcion_privada(), 5);
    }
}
Copied!

The #[cfg(test)] attribute tells the compiler: “If you are going to compile for production (cargo build), ignore this module completely. Only include it if I am running tests”. This saves space and compilation time in the final binary.

Controlling execution

cargo test compiles and runs everything in parallel. But sometimes you want more control:

  • Show println! output: By default, Rust hides what your functions print if the test passes. To always see it: cargo test -- --show-output
  • Run a single test: cargo test test_name
  • Run sequentially (if your tests touch files or databases and interfere with each other): cargo test -- --test-threads=1