• RFC505 по стилю документации
• Рекомендации для API по документационному тестированию
Модульные тесты тестируют по одному модулю изолированно: они малы и могут проверить не публичный код. Интеграционные тесты являются внешними для вашего пакета и используют только его открытый интерфейс, таким же образом, как и любой другой код. Их цель в том, чтобы проверить, что многие части вашей библиотеки работают корректно вместе.
Cargo ищет интеграционные тесты в каталоге tests после каталога src.
Файл src/lib.rs:
// Предположим, что наш пакет называется `adder`, для теста он будет внешним кодом.
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
Файл с тестом: tests/integration_test.rs:
// мы тестируем extern crate, как и любой другой код.
extern crate adder;
#[test]
fn test_add() {
assert_eq!(adder::add(3, 2), 5);
}
Запустить тесты можно командой cargo test:
$ cargo test
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
Running target/debug/deps/integration_test-bcd60824f5fbfe19
running 1 test
test test_add ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
Doc-tests adder
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
Каждый файл с исходным кодом в директории tests компилируется в отдельный пакет. Один из путей использовать некоторый общий код между интеграционными тестами - создать модуль с публичными функциями и импортировать их в тестах.
Файл tests/common.rs:
pub fn setup() {
// некоторый код для настройки, создание необходимых файлов/каталогов, запуск серверов.
}
Файл с тестом: tests/integration_test.rs
// мы тестируем extern crate, как и любой другой код.
extern crate adder;
// импорт общего модуля.
mod common;
#[test]
fn test_add() {
// использование общего кода.
common::setup();
assert_eq!(adder::add(3, 2), 5);
}
Модули с общим кодом следуют обычным правилам модулей. Общий модуль можно создать как tests/common/mod.rs.
Иногда возникает необходимость иметь зависимости только для тестов (примеры, бенчмарки). Такие зависимости добавляются в Cargo.toml в секцию [dev-dependencies]. Эти зависимости не распространяются как зависимости на другие пакеты, которые зависят от этого пакета.
Одним из таких примеров является пакет расширяющий стандартный макрос assert!. Файл Cargo.tomclass="underline"
# при стандартной сборке проекта данная зависимость не будет использоваться.
[dev-dependencies]
pretty_assertions = "0.4.0"
Файл src/lib.rs:
// внешний пакет используется только для тестирования
#[cfg(test)]
#[macro_use]
extern crate pretty_assertions;
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_add() {
assert_eq!(add(2, 3), 5);
}
}
Документация Cargo по указанию зависимостей.
В качестве введения в этот раздел процитируем официальную документацию, "нужно стараться минимизировать количество небезопасного кода в кодовой базе." Имея это в виду, давайте начнём! Небезопасные аннотации в Rust используются для обхода блокировок защиты, устанавливаемых компилятором; в частности, существует четыре основных варианта использования небезопасного кода:
• разыменование сырых указателей
• вызов функций или методов, которые являются unsafe (включая вызов функции через FFI см. предыдущую главу книги)
• доступ или изменение статических изменяемых переменных
• реализация небезопасных типажей
Сырые указатели * и ссылки &T имеют схожую функциональность, но ссылки всегда безопасны, потому что они гарантированно указывают на достоверные данные за счёт механизма проверки заимствований. Разыменование же сырого указателя можно выполнить только через небезопасный блок.