ควบคุมว่าจะรันเทสอย่างไร
เช่นเดียวกับที่ cargo run คอมไพล์โค้ดของคุณแล้วรัน binary ที่ได้ออกมา
cargo test คอมไพล์โค้ดของคุณใน test mode และรัน binary เทสที่ได้ออกมา
พฤติกรรมเริ่มต้นของ binary ที่ cargo test สร้างคือรันเทสทั้งหมดแบบ
ขนาน และจับ output ที่ถูกสร้างระหว่างการรันเทส ป้องกัน output ไม่ให้
แสดง และทำให้ง่ายขึ้นที่จะอ่าน output ที่เกี่ยวกับผลเทส อย่างไรก็ตาม
คุณระบุ option บน command line เพื่อเปลี่ยนพฤติกรรมเริ่มต้นนี้ได้
option บน command line บางตัวไปยัง cargo test และบางตัวไปยัง binary
เทสที่ได้ออกมา เพื่อแยกอาร์กิวเมนต์สองประเภทนี้ คุณ list อาร์กิวเมนต์
ที่ไปยัง cargo test ตามด้วยตัวคั่น -- แล้วตามด้วยตัวที่ไปยัง binary
เทส รัน cargo test --help แสดง option ที่คุณใช้กับ cargo test ได้
และรัน cargo test -- --help แสดง option ที่คุณใช้หลังตัวคั่นได้
option เหล่านี้ยังถูก document ใน
ส่วน “Tests” ของ The rustc Book
รันเทสแบบขนานหรือต่อเนื่อง
เมื่อคุณรันหลายเทส ตามค่าเริ่มต้นพวกมันรันแบบขนานโดยใช้เธรด แปลว่าพวก มันรันเสร็จเร็วขึ้น และคุณได้ feedback เร็วขึ้น เพราะเทสรันพร้อมกัน คุณ ต้องแน่ใจว่าเทสของคุณไม่ขึ้นต่อกัน หรือไม่ขึ้นกับ shared state ใด ๆ รวมถึง environment ที่ใช้ร่วม เช่น working directory ปัจจุบัน หรือ environment variable
ตัวอย่างเช่น สมมติแต่ละเทสของคุณรันโค้ดที่สร้างไฟล์บน disk ชื่อ test-output.txt และเขียนข้อมูลลงในไฟล์นั้น จากนั้นแต่ละเทสอ่านข้อมูล ในไฟล์นั้น และ assert ว่าไฟล์มีค่าเฉพาะที่ต่างกันในแต่ละเทส เพราะเทส รันพร้อมกัน เทสหนึ่งอาจ overwrite ไฟล์ในเวลาระหว่างที่อีกเทสกำลังเขียน และอ่านไฟล์ เทสที่สองจะ fail ไม่ใช่เพราะโค้ดไม่ถูกต้อง แต่เพราะเทส รบกวนกันระหว่างที่รันแบบขนาน วิธีแก้หนึ่งคือทำให้แน่ใจว่าแต่ละเทสเขียน ลงในไฟล์ต่างกัน อีกวิธีคือรันเทสทีละตัว
ถ้าคุณไม่อยากรันเทสแบบขนาน หรือถ้าคุณต้องการการควบคุมที่ละเอียดกว่า
เกี่ยวกับจำนวนเธรดที่ใช้ คุณส่ง flag --test-threads และจำนวนเธรด
ที่คุณต้องการใช้ให้ binary เทสได้ ดูตัวอย่างต่อไปนี้:
$ cargo test -- --test-threads=1
เราตั้งจำนวนเธรดเทสเป็น 1 บอกโปรแกรมไม่ให้ใช้ parallelism ใด ๆ การ
รันเทสด้วยเธรดเดียวจะใช้เวลานานกว่าการรันแบบขนาน แต่เทสจะไม่รบกวนกัน
ถ้าพวกมันแชร์ state
แสดง Output ของฟังก์ชัน
ตามค่าเริ่มต้น ถ้าเทสผ่าน test library ของ Rust จับอะไรก็ตามที่ถูก print
ออกไป standard output ตัวอย่างเช่น ถ้าเราเรียก println! ในเทส และเทส
ผ่าน เราจะไม่เห็น output ของ println! ใน terminal — เราจะเห็นเฉพาะ
บรรทัดที่ระบุว่าเทสผ่าน ถ้าเทส fail เราจะเห็นอะไรก็ตามที่ถูก print ออก
ไป standard output พร้อมส่วนที่เหลือของข้อความ failure
ตัวอย่างเช่น Listing 11-10 มีฟังก์ชันงี่เง่าที่ print ค่าของ parameter และ return 10 รวมถึงเทสที่ผ่านและเทสที่ fail
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);
}
}
println!เมื่อเรารันเทสเหล่านี้ด้วย cargo test เราจะเห็น output ต่อไปนี้:
$ 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`
สังเกตว่าไม่มีที่ไหนใน output นี้ที่เราเห็น I got the value 4 ซึ่ง
ถูก print เมื่อเทสที่ผ่านรัน output นั้นถูกจับไป output จากเทสที่ fail
I got the value 8 ปรากฏในส่วนของสรุป output เทส ซึ่งยังแสดงสาเหตุของ
failure ของเทสด้วย
ถ้าเราต้องการเห็นค่าที่ถูก print สำหรับเทสที่ผ่านด้วย เราบอก Rust ให้
แสดง output ของเทสที่สำเร็จด้วย --show-output ได้:
$ cargo test -- --show-output
เมื่อเรารันเทสใน Listing 11-10 อีกครั้งด้วย flag --show-output เราจะ
เห็น output ต่อไปนี้:
$ 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`
รันเทสบางส่วนตามชื่อ
การรัน test suite เต็มบางครั้งใช้เวลานาน ถ้าคุณทำงานกับโค้ดในส่วนเฉพาะ
คุณอาจต้องการรันเฉพาะเทสที่เกี่ยวข้องกับโค้ดนั้น คุณเลือกเทสที่จะรันโดย
ส่งชื่อหรือชื่อของเทสที่คุณต้องการรันให้ cargo test เป็นอาร์กิวเมนต์
ได้
เพื่อสาธิตวิธีรันเทสบางส่วน เราจะสร้างเทสสามตัวสำหรับฟังก์ชัน add_two
ก่อน ตามที่แสดงใน Listing 11-11 และเลือกตัวที่จะรัน
pub fn add_two(a: u64) -> u64 {
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);
}
}
ถ้าเรารันเทสโดยไม่ส่งอาร์กิวเมนต์ใด ๆ ดังที่เราเห็นก่อนหน้า เทสทั้งหมด จะรันแบบขนาน:
$ 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
รันเทสเดี่ยว
เราส่งชื่อของฟังก์ชันเทสใด ๆ ให้ cargo test เพื่อรันเฉพาะเทสนั้นได้:
$ 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
มีเฉพาะเทสที่ชื่อ one_hundred รัน — อีกสองเทสไม่ตรงชื่อนั้น output
เทสบอกเราว่าเรามีเทสเพิ่มที่ไม่รัน โดยแสดง 2 filtered out ที่ท้ายสุด
เราระบุชื่อหลายเทสในวิธีนี้ไม่ได้ — เฉพาะค่าแรกที่ให้กับ cargo test
จะถูกใช้ แต่มีวิธีรันหลายเทส
Filter เพื่อรันหลายเทส
เราระบุส่วนหนึ่งของชื่อเทสได้ และเทสใดที่ชื่อตรงกับค่านั้นจะถูกรัน
ตัวอย่างเช่น เพราะสองในชื่อเทสของเรามี add เรารันสองเทสเหล่านั้นได้
โดยรัน 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
คำสั่งนี้รันเทสทั้งหมดที่มี add ในชื่อ และ filter เทสที่ชื่อ
one_hundred ออก สังเกตว่าโมดูลที่เทสปรากฏกลายเป็นส่วนหนึ่งของชื่อเทส
ดังนั้นเรารันเทสทั้งหมดในโมดูลโดย filter บนชื่อของโมดูลได้
Ignore เทสยกเว้นถูกร้องขอเฉพาะ
บางครั้งเทสบางตัวอาจใช้เวลานานมากในการ execute ดังนั้นคุณอาจต้องการ
exclude พวกมันระหว่างการรัน cargo test ส่วนใหญ่ แทนที่จะ list เทส
ทั้งหมดที่คุณต้องการรันเป็นอาร์กิวเมนต์ คุณ annotate เทสที่ใช้เวลานาน
โดยใช้ attribute ignore เพื่อ exclude พวกมันแทน ดังที่แสดงที่นี่:
Filename: 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
}
}
หลัง #[test] เราเพิ่มบรรทัด #[ignore] ให้เทสที่เราต้องการ exclude
ตอนนี้เมื่อเรารันเทส it_works จะรัน แต่ expensive_test จะไม่รัน:
$ 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
ฟังก์ชัน expensive_test ถูก list เป็น ignored ถ้าเราต้องการรันเฉพาะ
เทสที่ถูก ignore เราใช้ 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 tests::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
โดยควบคุมว่าเทสไหนรัน คุณทำให้แน่ใจได้ว่าผล cargo test ของคุณจะกลับ
มาเร็ว เมื่อคุณอยู่ในจุดที่สมเหตุสมผลที่จะตรวจสอบผลของเทส ignored
และคุณมีเวลารอผล คุณรัน cargo test -- --ignored แทนได้ ถ้าคุณต้องการ
รันเทสทั้งหมดไม่ว่าจะถูก ignore หรือไม่ คุณรัน
cargo test -- --include-ignored ได้