Mocking

Для імітації широко використовується бібліотека Mockall. Вам потрібно рефакторити свій код, щоб використовувати трейти, які потім можна швидко імітувати:

use std::time::Duration;

#[mockall::automock]
pub trait Pet {
    fn is_hungry(&self, since_last_meal: Duration) -> bool;
}

#[test]
fn test_robot_dog() {
    let mut mock_dog = MockPet::new();
    mock_dog.expect_is_hungry().return_const(true);
    assert_eq!(mock_dog.is_hungry(Duration::from_secs(10)), true);
}
This slide should take about 5 minutes.
  • Mockall - рекомендована бібліотека для створення імітацій в Android (AOSP). На crates.io доступні й інші бібліотеки для імітації, зокрема для імітації HTTP-сервісів. Інші бібліотеки імітацій працюють подібно до Mockall, тобто вони дозволяють легко отримати імітаційну реалізацію заданого трейту.

  • Зауважте, що імітація дещо суперечлива: імітації дозволяють повністю ізолювати тест від його залежностей. Безпосереднім результатом є швидше і стабільніше виконання тесту. З іншого боку, імітатори можуть бути налаштовані неправильно і повертати результат, відмінний від того, який виводили б реальні залежності.

    Якщо це можливо, рекомендується використовувати реальні залежності. Наприклад, багато баз даних дозволяють налаштовувати бекенд в пам'яті. Це означає, що ви отримаєте правильну поведінку у ваших тестах, до того ж вони швидкі і автоматично прибиратимуть за собою.

    Аналогічно, багато веб-фреймворків дозволяють запускати сервер у процесі роботи, який прив'язується до випадкового порту на localhost. Завжди віддавайте перевагу цьому, а не імітаційному фреймворку, оскільки це допоможе вам протестувати ваш код у реальному середовищі.

  • Mockall не є частиною Rust Playground, тому вам потрібно запустити цей приклад у локальному середовищі. Використовуйте cargo add mockall для швидкого додавання Mockall до існуючого проекту Cargo.

  • Mockall має набагато більше функціональних можливостей. Зокрема, ви можете встановлювати очікування, які залежать від переданих аргументів. Тут ми використовуємо це, щоб імітувати кота, який зголоднів через 3 години після того, як його востаннє годували:

#[test]
fn test_robot_cat() {
    let mut mock_cat = MockPet::new();
    mock_cat
        .expect_is_hungry()
        .with(mockall::predicate::gt(Duration::from_secs(3 * 3600)))
        .return_const(true);
    mock_cat.expect_is_hungry().return_const(false);
    assert_eq!(mock_cat.is_hungry(Duration::from_secs(1 * 3600)), false);
    assert_eq!(mock_cat.is_hungry(Duration::from_secs(5 * 3600)), true);
}
  • Ви можете використати .times(n), щоб обмежити кількість викликів імітаційного методу до n --- імітація автоматично панікує при звільненні, якщо ця умова не виконується.