Тестирование приложений
Синтаксис тестов
Создание модуля тестирования
#![allow(unused)] fn main() { #[cfg(test)] // обязательный атрибут если пишем тест в этом же крейте, который тестируем mod tests {//это такой же модуль как и все другие use super::*; //обязательно для получения доступа к головным функциям #[test]//обязательно перед функцией тестирования fn it_works() { let result = add(2, 2); assert_eq!(result, 4); } } }
Запуск тестирования
cargo test #полный тест
cargo test it_works #перечисление функций теста
cargo test it #тест по контексту, запустит все функции которые имеют в названии it
cargo test -- --ignored #тест функций с пометкой ignore
Функциии проверки
assert!
assert! Возвращает true если выражение вернет true
#![allow(unused)] fn main() { assert!(larger.can_hold(&smaller)); }
assert_eq! и assert_ne!
assert_eq! Возвращает true если условие выполняется, assert_eq! возвращает true если не выполняется
#![allow(unused)] fn main() { assert_eq!(result, 4); }
Сообщение об ошибках в тесте
Второй параметр в макросе assert!
#![allow(unused)] fn main() { #[test] fn greeting_contains_name() { let result = greeting("Carol"); assert!( result.contains("Carol"), "Greeting did not contain name, value was `{result}`" //это сообщение будет выведено в случае ошибки ); } }
При ошибке в протоколе будут выведены все сообщения println!
В теле основной функции все println! будут выведены в протокол
Макрос should_panic
#![allow(unused)] fn main() { #[test] #[should_panic] fn greater_than_100() { Guess::new(200); } }
Перехватывает все panic! в программе. Если panic случился,то тест прошел. Не проверяет какой был panic.
expected
Будет искать по контексту, что написал panic, если значения совпадают,то тест прошел.
#![allow(unused)] fn main() { #[test] #[should_panic(expected = "less than or equal to 100")] fn greater_than_100() { Guess::new(200); } }
Использование Result<T, E> в тестах
#![allow(unused)] fn main() { #[test] fn it_works() -> Result<(), String> { let result = add(2, 2); if result == 4 { Ok(()) } else { Err(String::from("two plus two does not equal four")) } } }
Управление тестами
Не будет выполнять параллельно
cargo test -- --test-threads=1
Выполнит только в обном потоке. Не будет параллельности.
Напечатает все успешные тесты
cargo test -- --show-output
Игнорирование тестов
#![allow(unused)] fn main() { #[test] #[ignore] fn expensive_test() { // code that takes an hour to run } }
для вызова только проигнорированных тестов
cargo test -- --ignored
Организация тестов
Внутри тестируемого крейта
Перед модулем обязательный атрибут
#[cfg(test)]
#![allow(unused)] fn main() { 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); } } }
Интеграционные тесты
В Rust интеграционные тесты являются полностью внешними по отношению к вашей библиотеке. Они используют вашу библиотеку так же, как любой другой код, что означает, что они могут вызывать только функции, которые являются частью публичного API библиотеки.
adder
├── Cargo.lock
├── Cargo.toml
├── src
│ └── lib.rs
└── tests
└── integration_test.rs
- Внутри проекта создать директорию
tests - Создать несколько файлов с тестами
- В заголовке каждого крейта теста указать
use adder::add_two;— ссылку на тестируемый модуль как внешний - Модуль test создавать не нужно
#![allow(unused)] fn main() { use adder::add_two; #[test] fn it_adds_two() { let result = add_two(2); assert_eq!(result, 4); } }
Создание общих функций для тестов
Файл: tests/common.rs - должен быть в структуре tests. Чтобы модуль common больше не появлялся в результатах выполнения тестов, вместо файла tests/common.rs мы создадим файл tests/common/mod.rs.
#![allow(unused)] fn main() { pub fn setup() { // setup code specific to your library's tests would go here } }
Файлы в подкаталогах каталога tests не компилируются как отдельные крейты или не появляются в результатах выполнения тестов.
Пример вызова в тесте общей функции
#![allow(unused)] fn main() { use adder::add_two; mod common; #[test] fn it_adds_two() { common::setup(); let result = add_two(2); assert_eq!(result, 4); } }
Только библиотечные крейты могут предоставлять функции, которые можно использовать в других крейтах; бинарные крейты предназначены только для самостоятельного запуска.
Структура блочного тестового модуля
tests
├── helpers
│ ├── mod.rs
│ ├── test_module1.rs
│ ├── test_module2.rs
│ └── utils.rs
├── integration_test.rs
└── mod.rs
tests/mod.rs
#![allow(unused)] fn main() { // Объявляем модули (он один, если несколько папок, то перечисляем) mod helpers; //mod integration_test; // Реэкспортируем функции из библиотеки если нужно pub use test_learn::library_function; }
tests/helpers/mod.rs
#![allow(unused)] fn main() { // Объявляем подмодули в папке helpers mod test_module1; mod test_module2; //mod utils; }
Тестовые модули
#![allow(unused)] fn main() { use test_learn::module1; #[cfg(test)] mod tests { use super::*; #[test] fn test_add() { assert_eq!(module1::add(2, 3), 5); assert_eq!(module1::add(-1, 1), 0); assert_eq!(module1::add(0, 0), 0); } #[test] fn test_multiply() { assert_eq!(module1::multiply(2, 3), 6); assert_eq!(module1::multiply(5, 0), 0); assert_eq!(module1::multiply(-2, 3), -6); } #[test] fn test_edge_cases() { assert_eq!(module1::add(i32::MAX, 0), i32::MAX); } } }
Вывод тестов
cargo test
Compiling test_learn v0.1.0 (/home/edge/data/rs/test_learn)
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.05s
Running unittests src/lib.rs (target/debug/deps/test_learn-f7d50bd57ebc5cb3)
running 2 tests
test module1::tests::test_add ... ok
test module2::tests::test_calculator_new ... ok
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running tests/integration_test.rs (target/debug/deps/integration_test-a9701079e41f5aba)
running 2 tests
test test_integration ... ok
test test_library_function ... ok
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running tests/mod.rs (target/debug/deps/mod-26cc964da696d58a)
running 6 tests
test helpers::test_module1::tests::test_add ... ok
test helpers::test_module1::tests::test_edge_cases ... ok
test helpers::test_module2::tests::test_calculator_sequence ... ok
test helpers::test_module2::tests::test_calculator_add ... ok
test helpers::test_module1::tests::test_multiply ... ok
test helpers::test_module2::tests::test_calculator_new ... ok
test result: ok. 6 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests test_learn
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
cargo-test finished at Mon Nov 3 15:02:08, duration 0.10 s