Tests, boundaries and debugging
Tests, boundaries and debugging
A target-checking function should work at the boundary, not only for an easy passing mark. Rust’s test harness lets you encode expected behavior. A useful test describes a policy or error path rather than merely repeating each statement of the implementation.
What you’ll learn
- Test both sides of the target and valid-range boundaries.
- Distinguish program execution, tests and diagnostic output.
Tests, boundaries and debugging in action
74: below threshold
The ordinary executable prints the boundary decision for 75.
74: below threshold75 and 100: valid target
The test build separately runs four assertions: just below the threshold, at the threshold, at the valid maximum and above it.
75 and 100: valid target101: invalid mark
The last assertion prevents treating invalid large marks as valid passes.
101: invalid mark
Tests, boundaries and debugging explained
#[test] marks a test function. assert! checks a Boolean, and assert_eq! compares values. A cfg(test) module is included for test builds. Run cargo test, or rustc --edition=2024 --test main.rs -o school-tests and launch the test executable for this standalone file.
Work through the Rust program
Save the complete source as main.rs in the Rust environment. No standard input is needed.
Rust 2024 · complete program
fn meets_target(score: u32) -> bool { (75..=100).contains(&score) }
fn main() { println!("Target at 75: {}", meets_target(75)); }
#[cfg(test)]
mod tests {
use super::meets_target;
#[test]
fn target_boundaries() {
assert!(!meets_target(74));
assert!(meets_target(75));
assert!(meets_target(100));
assert!(!meets_target(101));
}
}Expected output
Target at 75: trueFollow the execution
The ordinary executable prints the boundary decision for 75. The test build separately runs four assertions: just below the threshold, at the threshold, at the valid maximum and above it. The last assertion prevents treating invalid large marks as valid passes.
Try the complete program in the Rust compiler. For tests, Cargo commands and local files, use the specified local environment.
Try a small variation
Your task: Why include the test for 101 rather than testing only 80?
Rust 2024 · complete program
fn meets_target(score: u32) -> bool { (75..=100).contains(&score) }
fn main() { println!("Target at 75: {}", meets_target(75)); }
#[cfg(test)]
mod tests {
use super::meets_target;
#[test]
fn target_boundaries() {
assert!(!meets_target(74));
assert!(meets_target(75));
assert!(meets_target(100));
assert!(!meets_target(101));
}
}Worked answer
It checks that the valid mark range has an upper boundary. A function using only score >= 75 would pass the easy case while incorrectly accepting 101.
Mistakes and limits to recognize
Dbg! writes a diagnostic representation to stderr and returns its expression’s value; it is not a replacement for tests. Integration tests can exercise public APIs, and documentation examples can be tested too. Nondeterministic services need controlled test inputs and explicit expectations.
Check your understanding
Take the idea into your next program
It checks that the valid mark range has an upper boundary. A function using only score >= 75 would pass the easy case while incorrectly accepting 101. Dbg! writes a diagnostic representation to stderr and returns its expression’s value; it is not a replacement for tests.
Further reading: the official Rust Book on tests, boundaries and debugging.
Reaching the recap and completing the quizzes are separate milestones.