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

ควบคุมว่าจะรันเทสอย่างไร

เช่นเดียวกับที่ 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

Filename: src/lib.rs
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);
    }
}
Listing 11-10: เทสสำหรับฟังก์ชันที่เรียก 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 และเลือกตัวที่จะรัน

Filename: src/lib.rs
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);
    }
}
Listing 11-11: สามเทสที่มีชื่อต่างกันสามชื่อ

ถ้าเรารันเทสโดยไม่ส่งอาร์กิวเมนต์ใด ๆ ดังที่เราเห็นก่อนหน้า เทสทั้งหมด จะรันแบบขนาน:

$ 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 ได้