يصرّف cargo test
شيفرتك البرمجية بالطريفة نفسها التي يصرّف فيها الأمر cargo run
شيفرتك البرمجية ويشغّلها، إلا أن cargo test
يصرّف الشيفرة البرمجية في نمط الاختبار ويشغّل ملف الاختبار الثنائي. السلوك الافتراضي للملف الثنائي الناتج عن cargo test
هو تشغيل جميع الاختبارات على التوازي والحصول على الخرج خلال تشغيل الاختبار ومنع الخرج من العرض مما يجعل من الخرج المتعلق بنتائج الاختبار أكثر وضوحًا، إلا أنه يمكنك كتابة خيارات في سطر الأوامر للتغيير من هذا السلوك الافتراضي.
تنتمي بعض الخيارات في سطر الأوامر إلى cargo test
بينما ينتمي بعضها لملف الاختبار الثنائي الناتج، وللفصل بين النوعين من الوسطاء نضع الوسطاء الخاصة بالأمر cargo test
متبوعةً بالفاصل --
ومن ثم الوسطاء الخاصة بملف الاختبار الثنائي. يعرض تنفيذ الأمر cargo test --help
الخيارات الممكن استخدامها مع cargo test
، بينما يعرض تنفيذ cargo test -- --help
الخيارات التي يمكنك استخدامها بعد الفاصل.
تشغيل الاختبارات على نحو متعاقب أو على التوازي
تُنفّذ الاختبارات على التوازي افتراضيًا عند تشغيل عدة اختبارات باستخدام خيوط threads، مما يعني أن تنفيذها سيكون سريعًا وستحصل على نتيجتك بصورةٍ أسرع، وبما أن الاختبارات تُنفّذ في الوقت ذاته فهذا يعني أنها يجب ألا تعتمد على بعضها بعضًا أو تحتوي على حالة مشتركة بما فيه بيئة مشتركة مثل مسار العمل الحالي current working directory أو متغيرات البيئة environment variables.
على سبيل المثال لنقل أن اختباراتك تنفذ شيفرة برمجية تُنشئ ملفًا على قرص باسم test-output.txt وتكتب بعض البيانات على هذا الملف، يقرأ عندها كل اختبار البيانات في ذلك الملف ويتأكد أن الملف يحتوي على قيمة معينة وهي قيمة مختلفة بحسب كل اختبار، ولأن الاختبارات تُشغّل في الوقت ذاته فهذا يعني أن أحد الاختبارات قد يكتب على محتويات الملف في وقت تنفيذ اختبار آخر وقراءته للملف وسيفشل عندها الاختبار الثاني ليس لعدم صحة الشيفرة البرمجية بل لأن الاختبارات أثرت على بعضها بعضًا عند تشغيلها على التوازي. يكمن أحد الحلول هنا بالتأكد أن كل اختبار يكتب إلى ملف مختلف، وهناك حلّ آخر يتمثل بتشغيل الاختبارات على التتالي كلّ حسب دوره.
إذا لم ترد تشغيل الاختبارات على التوازي، أو أردت تحكمًا أكبر على أرقام الخيوط التي تريد استخدامها فيمكنك عندئذ استخدام الراية --test-threads
متبوعةً بعدد الخيوط التي تريد استخدامها مع الاختبار الثنائي. ألقِ نظرةً على المثال التالي:
$ cargo test -- --test-threads=1
نضبط عدد خيوط الاختبار إلى "1"، وهذا يجعل البرنامج يعرف أننا لا نريد تشغيل الاختبارات على التوازي، إذ يستغرق تشغيل الاختبارات باستخدام خيط واحد وقتًا أكبر من تشغيلها على التوازي، إلا أن الاختبارات لن تتداخل في عمل بعضها بعضًا إذا تشاركت في حالة ما.
عرض خرج الدالة
تلتقط مكتبة اختبار رست تلقائيًا كل شيء يُطبع إلى الخرج القياسي إذا نجح الاختبار، على سبيل المثال إذا استدعينا println!
في اختبار ما ونجح هذا الاختبار فلن نرى خرج println!
في الطرفية بل سنرى فقط السطر الذي يشير إلى نجاح الاختبار، بينما سنرى ما طُبع إلى الخرج القياسي إذا فشل الاختبار مصحوبًا مع رسالة الفشل.
تحتوي الشيفرة 10 على مثال بسيط يحتوي على دالة تطبع قيمة معاملها وتُعيد 10 إضافةً إلى اختبار ينجح وآخر يفشل.
اسم الملف: 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!(10, value); } #[test] fn this_test_will_fail() { let value = prints_and_returns_10(8); assert_eq!(5, value); } }
[الشيفرة 10: اختبارات للدالة التي تستدعي println!
]
نحصل على الخرج التالي عند تشغيل هذه الاختبارات باستخدام cargo test
:
$ cargo test Compiling silly-function v0.1.0 (file:///projects/silly-function) Finished test [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 'main' panicked at 'assertion failed: `(left == right)` left: `5`, right: `10`', src/lib.rs:19:9 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'
لاحظ أن الخرج قد التُقط ولا يوجد فيه I got the value 4
وهو ما نطبعه عند تشغيل الاختبار الذي سينجح، بينما يظهر الخرج I got the value 8
من الاختبار الذي فشل في قسم خرج ملخص الاختبار الذي يوضح أيضًا سبب فشل الاختبار.
يمكننا اخبار رست بعرض خرج الاختبارات الناجحة باستخدام --show-output
إذا أردنا رؤية القيم المطبوعة للاختبارات الناجحة أيضًا.
$ cargo test -- --show-output
نحصل على الخرج التالي عند تشغيل الاختبارات الموجودة في الشيفرة 10 مجددًا باستخدام الراية --show-output
:
$ cargo test -- --show-output Compiling silly-function v0.1.0 (file:///projects/silly-function) Finished test [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 'main' panicked at 'assertion failed: `(left == right)` left: `5`, right: `10`', src/lib.rs:19:9 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'
تشغيل مجموعة من الاختبارات باستخدام اسم
قد يستغرق تشغيل كافة الاختبارات في بعض الأحيان وقتًا طويلًا، وقد تريد تشغيل مجموعة من الاختبارات مرتبطة فقط بجزئية معينة ضمن شيفرتك البرمجية، ويمكنك اختيار الاختبارات التي تريد تنفيذها بتمرير اسم الاختبار أو أسماء الاختبارات مثل وسطاء إلى الأمر cargo test
.
لتوضيح كيفية تشغيل مجموعة من الاختبارات نُنشئ أولًا ثلاث اختبارات للدالة add_two
كما هو موضح في الشيفرة 11 ونختار أي الاختبارات التي نريد تشغيلها.
اسم الملف: src/lib.rs
pub fn add_two(a: i32) -> i32 { a + 2 } #[cfg(test)] mod tests { use super::*; #[test] fn add_two_and_two() { assert_eq!(4, add_two(2)); } #[test] fn add_three_and_two() { assert_eq!(5, add_two(3)); } #[test] fn one_hundred() { assert_eq!(102, add_two(100)); } }
[الشيفرة 11: ثلاثة اختبارات مع ثلاثة أسماء مختلفة]
ستُنفّذ جميع الاختبارات على التوازي إذا شغّلنا الاختبارات دون تمرير أي وسطاء كما فعلنا سابقًا:
$ cargo test Compiling adder v0.1.0 (file:///projects/adder) Finished test [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 [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
فقط، إذ لم يطابق الاختباران الآخران هذا الاسم. يسمح لنا الخرج بمعرفة أن هناك المزيد من الاختبارات التي لم ننفذها بعرض 2 filtered out
في النهاية.
لا يمكننا تحديد أسماء اختبارات متعددة بهذه الطريقة، إذ تُستخدم القيمة الأولى المُعطاة للأمر cargo test
فقط، إلا أن هناك وسيلة أخرى لتنفيذ عدة اختبارات.
تنفيذ عدة اختبارات عن طريق الترشيح
يمكننا تحديد جزء من اسم اختبار بحيث يُنفّذ أي اختبار يطابق اسمه هذه القيمة. على سبيل المثال، يمكننا تنفيذ اختبارين من الاختبارات الثلاثة السابقة عن طريق add
وذلك بكتابة الأمر cargo test add
:
$ cargo test add Compiling adder v0.1.0 (file:///projects/adder) Finished test [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
في اسمها، ويستثني هنا الاختبار one_hundred
، كما يجب ملاحظة أن الوحدة التي تحتوي على الاختبار بداخلها تصبح جزءًا من اسم الاختبار، أي يمكننا تشغيل جميع الاختبارات الموجودة في وحدة معينة عن طريق استخدام اسمها.
تجاهل بعض الاختبارات إلا في حال طلبها
قد يكون لدينا في بعض الأحيان اختبارات معينة تستغرق وقتًا طويلًا لتنفيذها وقد ترغب باستثنائها من التشغيل عند كتابة الأمر cargo test
. يمكنك هنا استخدام توصيف الاختبارات التي تستغرق وقتًا طويلًا باستخدام السمة ignore
لاستثنائها بدلًا من كتابة جميع الاختبارات التي تريد تشغيلها مثل وسطاء باستثناء تلك الاختبارات، كما هو موضح هنا:
اسم الملف: src/lib.rs
#[test] fn it_works() { assert_eq!(2 + 2, 4); } #[test] #[ignore] fn expensive_test() { // شيفرة برمجية تستغرق عدة ساعات للتنفيذ }
نضيف #[ignore]
بعد سطر #[test]
ضمن الاختبار الذي نريد استثناءه، والآن عند تشغيل الاختبارات يُنفّذ الاختبار it_works
دون expensive_test
:
$ cargo test Compiling adder v0.1.0 (file:///projects/adder) Finished test [unoptimized + debuginfo] target(s) in 0.60s Running unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4) running 2 tests test expensive_test ... ignored test 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
تحت ignored
، وإذا أردنا تشغيل الاختبارات التي تجاهلناها فقط نكتب cargo test -- --ignored
:
$ cargo test -- --ignored Compiling adder v0.1.0 (file:///projects/adder) Finished test [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
نتأكد من خلال التحكم بالاختبارات التي تُنفَّذ من أن نتائج cargo test
ستكون سريعة، يمكنك تنفيذ cargo test -- --ignored
عندما تكون في حالة تريد فيها التحقق من الاختبارات التي تندرج تحت ignored
ولديك الوقت لانتظار النتائج، بينما تستطيع تنفيذ الأمر التالي إذا أردت تشغيل جميع الاختبارات المتجاهلة وغير المتجاهلة دفعةً واحدة.
cargo test -- --include-ignored
ترجمة -وبتصرف- لقسم من الفصل Writing Automated Tests من كتاب The Rust Programming Language.
أفضل التعليقات
لا توجد أية تعليقات بعد
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.