Для чего нужна данная статья? :
- Реализовать систему автоматизированной генерации и оптимизации тестов:
Обучение и генерация синтетических данных.
Генерация тестов.
Предсказание багов (заглушка в примере, в реальности — на реальных фичах).
Обнаружение аномалийОбучает VAE на "нормальных" данных.
Вычисляет reconstruction error.
Адаптивный анализСчитает изменения в git.
Измеряет время выполнения cargo test.
Feedback loop.
Визуализация.
Зачем Вам это уметь? :
- Увеличить надежность, производительность и удобство тестирования.
Основные виды тестирования
1. Модульное тестирование (Unit Testing)
Модульные тесты проверяют предлагаемые функции или модули на уровне их минимальной функциональности. Обычно такие тесты хранятся в том же файле, что и тестируемый код, внутри модуля #[cfg(test)].
#[cfg(test)]
mod tests {
#[test] fn it_works() {
assert_eq!(2 + 2, 4);
} }
2. Интеграционное тестирование
Интеграционные тесты проверяют взаимодействие между различными модулями или компонентами программы. Эти тестовые проекты рассматриваются как отдельные tests/в корне.
// tests/integration_test.rs
extern crate my_project;
#[test] fn test_integration() {
assert_eq!(my_project::add(2, 3), 5);
}
3. Документированное тестирование (Doc Testing)
Документированные тесты проверяют код кода, написанный в документации (например, в комментариях ///). Такие тесты автоматически проверяют корректность приведенных примеров.
/// Adds two numbers together.
///
/// # Examples
///
/// ```
/// let result = my_crate::add(2, 3);
/// assert_eq!(result, 5);
/// ``` pub fn add(a: i32, b: i32) -> i32 { a + b }
4. Тестирование производительности (Benchmarks)
В версии Rust, начиная с 1.64, поддержка бенчмарков находится в экспериментальном состоянии (нестабильно). Бенчмарки измеряют производительность отдельных функций. Для этого используется флаг #![feature(test)].
#![feature(test)] extern crate test;
#[bench] fn bench_add(b: &mut test::Bencher) {
b.iter(|| 2 + 2);
}
5. Регрессионное тестирование (Регрессионное тестирование).
Это тесты, которые создаются для проверки исправленных ошибок. Цель таких испытаний — убедиться, что ошибка не возникнет снова в будущем.
#[test] fn regression_test_for_issue_123()
{
assert_eq!(my_function(), expected_value);
}
6. Параметризованное тестирование
В Rust нет встроенной поддержки параметризованных тестов, но их можно настроить вручную с помощью итераций и использования данных таблиц. Обычно параметризованные тесты проверяют один и тот же код на разных входных данных.
#[test] fn test_with_parameters()
{
let test_cases = vec![(2, 3, 5), (1, 1, 2), (10, 10, 20)];
for (a, b, expected) in test_cases
{
assert_eq!(my_function(a, b), expected);
}
}
7. Тестирование использования макросов (Test Harnesses)
Для настройки и запуска тестов в Rust используется встроенная система тестов (тестовая обвязка), которая обрабатывает аннотации #[test]. Существует возможность писать собственные тестовые фреймворки на основе макросов.
8. Кастомные тесты
Rust также поддерживает планирование типовых тестов с использованием макросов и нестандартных стратегий тестирования. Например, можно написать тесты, которые будут проводиться в параллельных потоках или обеспечивать контрольные действия перед тестированием.
9. Тестирование с использованием cargo miri
Это инструмент для программ на Rust, который может выявить проблемы с безопасностью памяти (например, использование освобожденной памяти).
10. Фаззинг (Fuzzing)
Фаззинг — это метод тестирования с передачей случайных данных для выявления неожиданных событий программы. Для Rust существует библиотека для фаззинга — Cargo-Fuzz .
cargo fuzz init
11. Тестирование на основе свойств (Тестирование на свойствах)
Тестирование свойств последних инвариантов или свойств кода на различных случайных данных. Для этого в Rust можно использовать компонент быстрой проверки .
#[macro_use] extern crate quickcheck;
quickcheck!
{
fn prop_addition_commutative(a: i32, b: i32) -> bool
{ a + b == b + a
} }
Общая структура проекта тестирования с ML
testgen.rs — AdvancedTestGenerator (ядро генерации тестов)
Этот модуль отвечает за автоматическую генерацию unit-тестов на основе анализа AST кода (с помощью syn) и ML-руководства (вероятность багов + аномальность входов).
Trait TestGenerator
- Interface Segregation + Dependency Inversion: маленький интерфейс, зависимости только от абстракций.
Send + Sync — для thread-safe DI через Arc<Mutex<>>.
Struct AdvancedTestGenerator
Хранит зависимости через DI
- Single Responsibility: только генерация тестов.
Dependency Inversion: получает ML-модели через traits, а не конкретные типы.
Вспомогательные структуры и функцииFunctionInfo — DTO для информации о функции (имя, параметры, тип возврата, фичи).
- ComplexityVisitor — Visitor-паттерн (syn::visit::Visit) для вычисления цикломатической сложности.
- Извлекает метрики: количество параметров, сложность, приблизительное LOC.
Open/Closed: легко добавить новые метрики, не меняя существующий код. - Поддержка типов (is_supported_primitive, type_to_literal_cast):Фильтрует только примитивы (i32, i64, f32, f64).
Преобразует значения из тензора в Rust-литералы с кастом.
Основной метод: generate_tests
Поток выполнения (шаг за шагом):
Парсинг кода
- Извлекает все fn из файла.
Фильтрация и сбор информацииПропускает функции с неподдерживаемыми типами параметров.
Собирает FunctionInfo + фичи (для ML).
ML:Предсказание вероятности багов
- Для каждой функции вычисляется вероятность бага.
- Количество генерируемых тестов пропорционально этой вероятности:
ML:Ранжирование синтетических входов по аномальности
- Берёт наиболее аномальные входы (из VAE) — они с большей вероятностью найдут edge-кейсы.
Генерация тестов
Для каждой функции и выбранного входа:Извлекает значения из тензора (input_row.i(i).double_value()).
Генерирует литералы.
Создаёт тест с:catch_unwind — проверка на панику.
Для f32/f64 — проверка на NaN/inf.
Использует quote! для code generation.
Возврат
Возвращает Vec<TokenStream> — готовые токены для вставки в файл тестов.
main.rs — Оркестратор и точка входа
Traits для всех подсистем
Определены 8 traits (SyntheticDataGenerator, BugPredictor, TestGenerator, ...).
Все Send + Sync для параллелизма и DI.
Конкретные реализацииWGAN_GP → генерация данных.
MLPBugPredictor → предсказание багов.
VariationalAutoencoder → аномалии.
GitChangeDetector, SimplePerformanceAnalyzer, JsonFeedbackCollector, PlottersDashboardVisualizer.
Builder-паттерн с DI
- Open/Closed + Dependency Inversion: легко добавлять/заменять компоненты.
Некоторые компоненты имеют default (например, GitChangeDetector).
Struct TestAutomationOrchestrator
Хранит все компоненты через Shared<T> = Arc<Mutex<T>>.
Метод run() — основной цикл автоматизации