Rust на примерах — страница 64 из 65

Модульные тесты тестируют по одному модулю изолированно: они малы и могут проверить не публичный код. Интеграционные тесты являются внешними для вашего пакета и используют только его открытый интерфейс, таким же образом, как и любой другой код. Их цель в том, чтобы проверить, что многие части вашей библиотеки работают корректно вместе.

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.

dev-dependencies

Иногда возникает необходимость иметь зависимости только для тестов (примеры, бенчмарки). Такие зависимости добавляются в Cargo.toml в секцию [dev-dependencies]. Эти зависимости не распространяются как зависимости на другие пакеты, которые зависят от этого пакета.

Одним из таких примеров является пакет расширяющий стандартный макрос assert!. Файл Cargo.toml:

# при стандартной сборке проекта данная зависимость не будет использоваться.

[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 имеют схожую функциональность, но ссылки всегда безопасны, потому что они гарантированно указывают на достоверные данные за счёт механизма проверки заимствований. Разыменование же сырого указателя можно выполнить только через небезопасный блок.

fn main() {

let raw_p: *const u32 = &10;

unsafe {

assert!(*raw_p == 10);

}

}

הההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההה

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Вызов небезопасных функций

Некоторые функции могут быть объявлены как unsafe, то есть за корректность этого кода несёт ответственность программист, написавший его, вместо компилятора. Пример - это метод std::slice::from_raw_parts, который создаст срез из указателя на первый элемент и длины.

use std::slice;

fn main() {

let some_vector = vec![1, 2, 3, 4];

let pointer = some_vector.as_ptr();

let length = some_vector.len();

unsafe {

let my_slice: &[u32] = slice::from_raw_parts(pointer, length);

assert_eq!(some_vector.as_slice(), my_slice);

}

}

הההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההה

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Для slice::from_raw_parts одно из предположений, которое должно быть поддержано, что переданный указатель указывает на допустимую память и что в памяти лежит значение правильного типа. Если эти инварианты не поддерживаются, то поведение программы не определено, и неизвестно, что произойдёт.

Совместимость

Rust быстро развивается и из-за этого могут возникнуть определённые проблемы совместимости, не смотря на усилия по обеспечению обратной совместимости везде, где это возможно.