Writing Tests in Rust

Writing Tests in Rust

Introduction

We know that Rust has a very interactive & intelligent compiler, who'll notify us about all the errors, and warnings (what we ignore). But it cannot notify us about the issues in our business logic. So we should write tests (that we don't want to, but we should!)

Examples

Let's write a simple test, of adding two numbers. And if we initialize a library crate instead of a binary crate, this is something we'll get by default.

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);
    }
}

So we have a function that adds two numbers. Then we have a tests module, and inside we have a test it_works

we're storing the sum of two numbers in result and checking if result is equal to 4 or not. assert_eq! is a macro that checks equality for us.

Tests are generally written in a separate tests folder. We'll be getting the test results, by cargo test not cargo run

    Finished test [unoptimized + debuginfo] target(s) in 0.14s
     Running unittests src/lib.rs (target/debug/deps/simple_tests-2cf9939b509694d3)

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

   Doc-tests simple_tests

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

So we only have 1 test, and it passed, no tests failed or ignored.

Now let's fail 1 test.

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 not_works() {
        let result = add(2, 2);
        assert_eq!(result, 5);
    }
}

Added another test fn not_works.

    Finished test [unoptimized + debuginfo] target(s) in 0.00s
     Running unittests src/lib.rs (target/debug/deps/simple_tests-2cf9939b509694d3)

running 2 tests
test tests::it_works ... ok
test tests::not_works ... FAILED

failures:

---- tests::not_works stdout ----
thread 'tests::not_works' panicked at src/lib.rs:18:9:
assertion `left == right` failed
  left: 4
 right: 5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    tests::not_works

test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

error: test failed, to rerun pass `--lib`

And we have tests 1 passed, 1 failed.

In Rust the expected & actual values are called left & right, and that's what we have in the failure message.

Now we can also use panic! to make a test fail.

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 not_works() {
        let result = add(2, 2);
        assert_eq!(result, 5);
    }

    #[test]
    fn panics() {
        panic!("Test function panicked!");
    }
}

Output:

    Finished test [unoptimized + debuginfo] target(s) in 0.23s
     Running unittests src/lib.rs (target/debug/deps/simple_tests-2cf9939b509694d3)

running 3 tests
test tests::it_works ... ok
test tests::panics ... FAILED
test tests::not_works ... FAILED

failures:

---- tests::panics stdout ----
thread 'tests::panics' panicked at src/lib.rs:23:9:
Test function panicked!

---- tests::not_works stdout ----
thread 'tests::not_works' panicked at src/lib.rs:18:9:
assertion `left == right` failed
  left: 4
 right: 5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    tests::not_works
    tests::panics

test result: FAILED. 1 passed; 2 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

error: test failed, to rerun pass `--lib`

Conclusion

Now these tests are very simple. It adds two numbers, something we'll do a lot, but hopefully will never write a test for in a real business. Then we intentially create a panic!

We'll cover how real tests are written when we build some Rusty apps, and they're as different as different business logic. This is only an introduction on how to write tests. That's all for now, and Cheers!