Kiểm Soát Cách Chạy Các Bài Kiểm Thử

Giống như cargo run biên dịch code của bạn và sau đó chạy tệp nhị phân kết quả, cargo test biên dịch code của bạn ở chế độ kiểm thử và chạy tệp nhị phân kiểm thử kết quả. Hành vi mặc định của tệp nhị phân được tạo ra bởi cargo test là chạy tất cả các bài kiểm thử song song và thu thập kết quả được tạo ra trong quá trình chạy kiểm thử, ngăn chặn kết quả được hiển thị và làm cho việc đọc kết quả liên quan đến kết quả kiểm thử dễ dàng hơn. Tuy nhiên, bạn có thể chỉ định các tùy chọn dòng lệnh để thay đổi hành vi mặc định này.

Một số tùy chọn dòng lệnh được chuyển tới cargo test, và một số được chuyển tới tệp nhị phân kiểm thử kết quả. Để phân tách hai loại đối số này, bạn liệt kê các đối số chuyển tới cargo test theo sau là dấu phân tách -- và sau đó là các đối số chuyển tới tệp nhị phân kiểm thử. Chạy cargo test --help hiển thị các tùy chọn bạn có thể sử dụng với cargo test, và chạy cargo test -- --help hiển thị các tùy chọn bạn có thể sử dụng sau dấu phân tách. Những tùy chọn đó cũng được ghi lại trong phần "Tests" của sách rustc.

Chạy Các Bài Kiểm Thử Song Song hoặc Tuần Tự

Khi bạn chạy nhiều bài kiểm thử, theo mặc định chúng chạy song song sử dụng các thread, có nghĩa là chúng chạy xong nhanh hơn và bạn nhận được phản hồi nhanh hơn. Vì các bài kiểm thử đang chạy cùng lúc, bạn phải đảm bảo rằng các bài kiểm thử của bạn không phụ thuộc vào nhau hoặc vào bất kỳ trạng thái chung nào, bao gồm cả môi trường chung, chẳng hạn như thư mục làm việc hiện tại hoặc biến môi trường.

Ví dụ, giả sử mỗi bài kiểm thử của bạn chạy một số code tạo ra một tệp trên đĩa có tên test-output.txt và viết một số dữ liệu vào tệp đó. Sau đó, mỗi bài kiểm thử đọc dữ liệu trong tệp đó và khẳng định rằng tệp chứa một giá trị cụ thể, khác nhau trong mỗi bài kiểm thử. Vì các bài kiểm thử chạy cùng lúc, một bài kiểm thử có thể ghi đè lên tệp trong khoảng thời gian giữa một bài kiểm thử khác đang viết và đọc tệp. Bài kiểm thử thứ hai sẽ thất bại, không phải vì code không đúng mà vì các bài kiểm thử đã can thiệp vào nhau trong quá trình chạy song song. Một giải pháp là đảm bảo mỗi bài kiểm thử ghi vào một tệp khác nhau; giải pháp khác là chạy các bài kiểm thử từng cái một.

Nếu bạn không muốn chạy các bài kiểm thử song song hoặc nếu bạn muốn kiểm soát chi tiết hơn về số lượng thread được sử dụng, bạn có thể gửi cờ --test-threads và số thread bạn muốn sử dụng cho tệp nhị phân kiểm thử. Hãy xem ví dụ sau:

$ cargo test -- --test-threads=1

Chúng ta đặt số thread kiểm thử thành 1, báo cho chương trình không sử dụng bất kỳ tính năng song song nào. Chạy các bài kiểm thử bằng một thread sẽ mất nhiều thời gian hơn so với chạy chúng song song, nhưng các bài kiểm thử sẽ không can thiệp vào nhau nếu chúng chia sẻ trạng thái.

Hiển Thị Kết Quả Hàm

Theo mặc định, nếu một bài kiểm thử thành công, thư viện kiểm thử của Rust sẽ thu thập bất kỳ thứ gì được in ra đầu ra tiêu chuẩn. Ví dụ, nếu chúng ta gọi println! trong một bài kiểm thử và bài kiểm thử thành công, chúng ta sẽ không thấy kết quả println! trong terminal; chúng ta sẽ chỉ thấy dòng cho biết bài kiểm thử đã thành công. Nếu một bài kiểm thử thất bại, chúng ta sẽ thấy bất cứ thứ gì đã được in ra đầu ra tiêu chuẩn cùng với phần còn lại của thông báo lỗi.

Ví dụ, Listing 11-10 có một hàm ngớ ngẩn in ra giá trị của tham số và trả về 10, cũng như một bài kiểm thử thành công và một bài kiểm thử thất bại.

fn prints_and_returns_10(a: i32) -> i32 {
    println!("I got the value {a}");
    10
}

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

    #[test]
    fn this_test_will_pass() {
        let value = prints_and_returns_10(4);
        assert_eq!(value, 10);
    }

    #[test]
    fn this_test_will_fail() {
        let value = prints_and_returns_10(8);
        assert_eq!(value, 5);
    }
}

Khi chúng ta chạy các bài kiểm thử này với cargo test, chúng ta sẽ thấy kết quả sau:

$ cargo test
   Compiling silly-function v0.1.0 (file:///projects/silly-function)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.58s
     Running unittests src/lib.rs (target/debug/deps/silly_function-160869f38cff9166)

running 2 tests
test tests::this_test_will_fail ... FAILED
test tests::this_test_will_pass ... ok

failures:

---- tests::this_test_will_fail stdout ----
I got the value 8

thread 'tests::this_test_will_fail' panicked at src/lib.rs:19:9:
assertion `left == right` failed
  left: 10
 right: 5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    tests::this_test_will_fail

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`

Lưu ý rằng không có chỗ nào trong kết quả này chúng ta thấy I got the value 4, được in ra khi bài kiểm thử thành công chạy. Kết quả đó đã bị thu thập. Kết quả từ bài kiểm thử thất bại, I got the value 8, xuất hiện trong phần tóm tắt kết quả kiểm thử, phần này cũng hiển thị nguyên nhân của việc thất bại kiểm thử.

Nếu chúng ta muốn thấy các giá trị được in ra cho các bài kiểm thử thành công, chúng ta có thể báo cho Rust hiển thị kết quả của các bài kiểm thử thành công bằng --show-output:

$ cargo test -- --show-output

Khi chúng ta chạy lại các bài kiểm thử trong Listing 11-10 với cờ --show-output, chúng ta thấy kết quả sau:

$ cargo test -- --show-output
   Compiling silly-function v0.1.0 (file:///projects/silly-function)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.60s
     Running unittests src/lib.rs (target/debug/deps/silly_function-160869f38cff9166)

running 2 tests
test tests::this_test_will_fail ... FAILED
test tests::this_test_will_pass ... ok

successes:

---- tests::this_test_will_pass stdout ----
I got the value 4


successes:
    tests::this_test_will_pass

failures:

---- tests::this_test_will_fail stdout ----
I got the value 8

thread 'tests::this_test_will_fail' panicked at src/lib.rs:19:9:
assertion `left == right` failed
  left: 10
 right: 5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    tests::this_test_will_fail

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`

Chạy Một Tập Con Các Bài Kiểm Thử Theo Tên

Đôi khi, việc chạy một bộ kiểm thử đầy đủ có thể mất nhiều thời gian. Nếu bạn đang làm việc trên code trong một khu vực cụ thể, bạn có thể muốn chỉ chạy các bài kiểm thử liên quan đến code đó. Bạn có thể chọn các bài kiểm thử để chạy bằng cách truyền cho cargo test tên hoặc các tên của (các) bài kiểm thử bạn muốn chạy làm đối số.

Để minh họa cách chạy một tập con các bài kiểm thử, trước tiên chúng ta sẽ tạo ba bài kiểm thử cho hàm add_two của chúng ta, như được hiển thị trong Listing 11-11, và chọn những bài nào để chạy.

pub fn add_two(a: usize) -> usize {
    a + 2
}

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

    #[test]
    fn add_two_and_two() {
        let result = add_two(2);
        assert_eq!(result, 4);
    }

    #[test]
    fn add_three_and_two() {
        let result = add_two(3);
        assert_eq!(result, 5);
    }

    #[test]
    fn one_hundred() {
        let result = add_two(100);
        assert_eq!(result, 102);
    }
}

Nếu chúng ta chạy các bài kiểm thử mà không truyền bất kỳ đối số nào, như chúng ta đã thấy trước đây, tất cả các bài kiểm thử sẽ chạy song song:

$ cargo test
   Compiling adder v0.1.0 (file:///projects/adder)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.62s
     Running unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4)

running 3 tests
test tests::add_three_and_two ... ok
test tests::add_two_and_two ... ok
test tests::one_hundred ... ok

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

   Doc-tests adder

running 0 tests

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

Chạy Các Bài Kiểm Thử Đơn Lẻ

Chúng ta có thể truyền tên của bất kỳ hàm kiểm thử nào cho cargo test để chỉ chạy bài kiểm thử đó:

$ cargo test one_hundred
   Compiling adder v0.1.0 (file:///projects/adder)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.69s
     Running unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4)

running 1 test
test tests::one_hundred ... ok

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

Chỉ có bài kiểm thử có tên one_hundred được chạy; hai bài kiểm thử còn lại không khớp với tên đó. Kết quả kiểm thử cho chúng ta biết rằng chúng ta có nhiều bài kiểm thử không được chạy bằng cách hiển thị 2 filtered out ở cuối.

Chúng ta không thể chỉ định tên của nhiều bài kiểm thử theo cách này; chỉ có giá trị đầu tiên được cung cấp cho cargo test sẽ được sử dụng. Nhưng có một cách để chạy nhiều bài kiểm thử.

Lọc Để Chạy Nhiều Bài Kiểm Thử

Chúng ta có thể chỉ định một phần của tên bài kiểm thử, và bất kỳ bài kiểm thử nào có tên khớp với giá trị đó sẽ được chạy. Ví dụ, vì hai trong số các bài kiểm thử của chúng ta có tên chứa add, chúng ta có thể chạy hai bài đó bằng cách chạy cargo test add:

$ cargo test add
   Compiling adder v0.1.0 (file:///projects/adder)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.61s
     Running unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4)

running 2 tests
test tests::add_three_and_two ... ok
test tests::add_two_and_two ... ok

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

Lệnh này đã chạy tất cả các bài kiểm thử có add trong tên và lọc ra bài kiểm thử có tên one_hundred. Cũng lưu ý rằng module mà bài kiểm thử xuất hiện trở thành một phần của tên bài kiểm thử, vì vậy chúng ta có thể chạy tất cả các bài kiểm thử trong một module bằng cách lọc theo tên của module.

Bỏ Qua Một Số Bài Kiểm Thử Trừ Khi Có Yêu Cầu Cụ Thể

Đôi khi một số bài kiểm thử cụ thể có thể tốn rất nhiều thời gian để thực hiện, vì vậy bạn có thể muốn loại trừ chúng trong hầu hết các lần chạy cargo test. Thay vì liệt kê làm đối số tất cả các bài kiểm thử bạn muốn chạy, bạn có thể thay vào đó chú thích các bài kiểm thử tốn thời gian bằng thuộc tính ignore để loại trừ chúng, như được hiển thị ở đây:

Tên tệp: src/lib.rs

pub fn add(left: u64, right: u64) -> u64 {
    left + right
}

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

    #[test]
    fn it_works() {
        let result = add(2, 2);
        assert_eq!(result, 4);
    }

    #[test]
    #[ignore]
    fn expensive_test() {
        // code that takes an hour to run
    }
}

Sau #[test], chúng ta thêm dòng #[ignore] cho bài kiểm thử mà chúng ta muốn loại trừ. Bây giờ khi chúng ta chạy các bài kiểm thử, it_works chạy, nhưng expensive_test thì không:

$ cargo test
   Compiling adder v0.1.0 (file:///projects/adder)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.60s
     Running unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4)

running 2 tests
test tests::expensive_test ... ignored
test tests::it_works ... ok

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

   Doc-tests adder

running 0 tests

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

Hàm expensive_test được liệt kê là ignored (bị bỏ qua). Nếu chúng ta chỉ muốn chạy các bài kiểm thử bị bỏ qua, chúng ta có thể sử dụng cargo test -- --ignored:

$ cargo test -- --ignored
   Compiling adder v0.1.0 (file:///projects/adder)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.61s
     Running unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4)

running 1 test
test expensive_test ... ok

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

   Doc-tests adder

running 0 tests

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

Bằng cách kiểm soát các bài kiểm thử được chạy, bạn có thể đảm bảo rằng kết quả cargo test của bạn sẽ được trả về nhanh chóng. Khi bạn ở một điểm mà việc kiểm tra kết quả của các bài kiểm thử ignored có ý nghĩa và bạn có thời gian để chờ đợi kết quả, bạn có thể chạy cargo test -- --ignored thay thế. Nếu bạn muốn chạy tất cả các bài kiểm thử cho dù chúng có bị bỏ qua hay không, bạn có thể chạy cargo test -- --include-ignored.