Тестирование

Прекрасно, теперь, когда у нас есть push и pop, мы можем протестировать наш стек! Для Rust и cargo тестирование — это гражданин первого класса, так что всё должно быть очень просто. Достаточно написать функцию и пометить её аннотацией #[test].

В целом, мы пытаемся размещать наши тесты рядом с тестируемым кодом, как это принято в сообществе Rust. Тем не менее, обычно мы пишем тесты в новом пространстве имён, чтобы избежать конфликтов с «реальным» кодом.

Ключевое слово mod можно использовать не только для объявления внешнего модуля (как мы сделали это с first.rs и lib.rs), но и для создания встроенного модуля:

// в first.rs

mod test {
    #[test]
    fn basics() {
        // написать
    }
}

Мы запускаем тесты командой cargo test.

> cargo test
   Compiling lists v0.1.0 (/Users/ADesires/dev/temp/lists)
    Finished dev [unoptimized + debuginfo] target(s) in 1.00s
     Running /Users/ADesires/dev/lists/target/debug/deps/lists-86544f1d97438f1f

running 1 test
test first::test::basics ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
; 0 filtered out

Ура, наш тест, который ничего не проверяет, успешно пройден! Теперь давайте напишем какую-нибудь проверку. В этом нам поможет макрос assert_eq!. В нём нет никакой особой магии тестирования. Он всего навсего сравнивает две штуки, которые ему передали, и паникует, если они не совпадают. Да, мы сигнализируем о проблеме при помощи паники!

mod test {
    #[test]
    fn basics() {
        let mut list = List::new();

        // Проверяем, что пустой список ведёт себя правильно
        assert_eq!(list.pop(), None);

        // Заполняем список
        list.push(1);
        list.push(2);
        list.push(3);

        // Проверяем обычное удаление
        assert_eq!(list.pop(), Some(3));
        assert_eq!(list.pop(), Some(2));

        // Вставляем новые значения, просто чтобы проверить, что ничего не сломается
        list.push(4);
        list.push(5);

        // Проверяем обычное удаление
        assert_eq!(list.pop(), Some(5));
        assert_eq!(list.pop(), Some(4));

        // Проверяем граничный случай
        assert_eq!(list.pop(), Some(1));
        assert_eq!(list.pop(), None);
    }
}
> cargo test

error[E0433]: failed to resolve: use of undeclared type or module `List`
  --> src/first.rs:43:24
   |
43 |         let mut list = List::new();
   |                        ^^^^ use of undeclared type or module `List`


Ой! Поскольку мы создали новый модуль, в него надо явным образом импортировать List.

mod test {
    use super::List;
    // всё остальное точно также
}
> cargo test

warning: unused import: `super::List`
  --> src/first.rs:45:9
   |
45 |     use super::List;
   |         ^^^^^^^^^^^
   |
   = note: #[warn(unused_imports)] on by default

    Finished dev [unoptimized + debuginfo] target(s) in 0.43s
     Running /Users/ADesires/dev/lists/target/debug/deps/lists-86544f1d97438f1f

running 1 test
test first::test::basics ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
; 0 filtered out

Да!

Хотя, что это за предупреждение?.. В нашем тесте мы используем List явным образом!

...но только при запуске тестов! Чтобы угодить компилятору (и быть дружелюбным к другим программистам) мы должны явно указать, что весь модуль test должен быть скомпилирован только когда мы запускаем тесты.

#[cfg(test)]
mod test {
    use super::List;
    // всё остальное точно также
}

На этом тестирование завершено!