RustArena

Step 1 of 6

Your first test

So far you've checked your programs by running them and reading the output. That works once. A test is a small function that checks some code automatically, so you can rerun every check in a second, every time anything changes. Professional Rust code is full of them, and cargo test runs them all.

Checks you already know: assert!

The assert macros panic when a check fails, and do nothing when it passes:

fn ticket_price(age: u32) -> u32 {
    if age < 12 { 500 } else { 1200 }
}

fn main() {
    assert_eq!(ticket_price(8), 500);
    assert_eq!(ticket_price(40), 1200);
    assert!(ticket_price(11) < ticket_price(12));
    assert_ne!(ticket_price(0), 0);
    println!("all checks passed");
}
all checks passed

Turning checks into tests

Checks in main are clumsy: the first failure stops everything, and they run every time the program runs. Real tests live in their own functions, marked with the #[test] attribute:

fn ticket_price(age: u32) -> u32 {
    if age < 12 { 500 } else { 1200 }
}

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

    #[test]
    fn children_pay_500() {
        assert_eq!(ticket_price(8), 500);
    }

    #[test]
    fn adults_pay_1200() {
        assert_eq!(ticket_price(40), 1200);
    }
}
$ cargo test
   Compiling tickets v0.1.0 (/home/you/tickets)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.42s
     Running unittests src/main.rs (target/debug/deps/tickets-1f2e3d4c5b6a7980)

running 2 tests
test tests::adults_pay_1200 ... ok
test tests::children_pay_500 ... ok

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

Piece by piece:

Test exercises on this site

In test exercises you write #[test] functions, and Check runs cargo test. Your code doesn't need a fn main, because the test runner brings its own. Then the site does something extra: it plants bugs, called mutants, in the code you're testing (one at a time) and runs your tests again. Each mutant must make at least one of your tests fail. A test suite that passes no matter what the code does isn't protecting anything.

Common mistakes

Your task

ticket_price has three prices: children under 12 pay 500, seniors aged 65 and over pay 700, everyone else pays 1200. Write three tests in the tests module, one for each kind of visitor. Don't change ticket_price.

Challenge 2

is_valid_username accepts names of 3 to 12 characters (counting characters, not bytes) made only of lowercase letters, digits and _. Write at least 3 tests that check valid and invalid names. Use assert! for names that should pass and assert!(!...) for names that should fail.

Challenge 3

cart_total adds up the prices in a cart (in cents) and adds a 499 cent shipping fee, but shipping is free when the items cost 5000 or more. An empty cart costs 0 (no shipping). Write at least 3 tests, including one for the empty cart. Use assert_eq!. There's no tests module in the starter this time: write it yourself.

Next: assert_eq!, messages, and comparing your own types