Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Lesson 9 — Testing, Logging, and Debugging


🎯 Learning Objectives

  • Hiểu cách viết test trong Rust (unit, integration).
  • Biết cách sử dụng crate log, env_logger cho logging.
  • Làm quen với kỹ thuật debug: macro dbg!, logging có cấp độ, và test assert.

✅ Explanation & Key Concepts

Unit Test vs Integration Test

  • Unit test: kiểm tra từng hàm/module, nằm trong cùng file với code.
  • Integration test: kiểm tra hành vi tổng thể, nằm trong tests/ folder.

Logging

  • Dùng crate log (API) và backend như env_logger.
  • Gọi env_logger::init() trong main.
  • Mức log: error, warn, info, debug, trace.

Debugging

  • Macro dbg!(expr) in giá trị + vị trí.
  • Compiler hỗ trợ backtrace (RUST_BACKTRACE=1).

💻 Example Implementation

Cargo.toml


[package]
name = "lesson09_testing_logging"
version = "0.1.0"
edition = "2024"

[dependencies]
log = "0.4"
env_logger = "0.11"

[dev-dependencies]
once_cell = "1"

src/main.rs


// cargo-deps: log="0.4", env_logger="0.11", once_cell="1"
use log::{info, debug, error};


fn add(a: i32, b: i32) -> i32 {
    info!("Adding {a} and {b}");
    a + b
}


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


fn main() {
    env_logger::init();


    info!("Starting app");
    let sum = add(4, 5);
    debug!("Sum = {sum}");


    match divide(10, 0) {
        Some(v) => info!("Result = {v}"),
        None => error!("Division failed"),
    }


    dbg!(sum);
}

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

    static INIT: Lazy<()> = Lazy::new(|| {
        let _ = env_logger::builder().is_test(true).try_init();
    });

    #[test]
    fn test_add() {
        Lazy::force(&INIT);
        assert_eq!(add(2, 3), 5);
    }

    #[test]
    fn test_divide_by_zero() {
        Lazy::force(&INIT);
        assert_eq!(divide(10, 0), None);
    }
}

🛠️ Hands-on Exercises

  1. Basic: Viết test cho hàm multiply và kiểm tra kết quả đúng.
  2. Intermediate: Dùng env_logger để log khi test chạy (sử dụng once_cell để init logger 1 lần).
  3. Challenge: Tạo custom macro check! (như assert! nhưng log thêm info khi fail).

🐞 Common Pitfalls & Debugging Tips

Common Pitfalls

  • Quên gọi env_logger::init() → không thấy log.
  • Không set RUST_BACKTRACE=1 khi debug panic.
  • Ghi log quá nhiều (trace!) gây noise và giảm performance.

🔄 Migration Notes (Rust 2024+)

Migration Guidance

  • Crate logenv_logger vẫn ổn định.
  • Có thể dùng tracing crate cho log nâng cao.
  • Rust 2024 cải thiện macro hygiene, giúp macro custom an toàn hơn.

📚 References


❓ Q & A — Câu hỏi thường gặp

Q1. Testing trong Rust khác gì so với ngôn ngữ khác?

  • Rust biên dịch test cùng binary và chạy bằng cargo test. Trình biên dịch tối ưu để test không ảnh hưởng đến bản release.

Q2. Vì sao nên dùng log thay vì println!?

  • log hỗ trợ nhiều cấp độ log, có thể lọc linh hoạt, định dạng rõ ràng — phù hợp cho production.

Q3. Làm sao debug hiệu quả mà không ảnh hưởng hiệu năng?

  • Dùng dbg! trong quá trình phát triển, sau đó chuyển sang debug! hoặc info! có thể lọc qua biến môi trường (RUST_LOG=debug).

📖 Glossary of Terms

Key Terms

  • Unit test: kiểm tra logic nhỏ, nằm trong file code.
  • Integration test: test tổng thể, nằm trong thư mục tests/.
  • Logger: hệ thống ghi log với cấp độ.
  • dbg!: macro in giá trị + vị trí khi chạy.

🌿 Wisdom Note

Đạo Đức Kinh — Chương 71

Tri bất tri, thượng.

To know that you do not know is the highest wisdom.

Debugging cũng vậy: nhận ra điều mình chưa biết là khởi đầu của hiểu biết.