Я пытаюсь разработать какой-нибудь инструмент CLI для отслеживания времени в Rust. Я пытаюсь покрыть большую часть кода модульными тестами и застрял в том, как использовать объекты-заглушки с ограничениями прав доступа Rusts.
У меня есть тип Tracker
, который имеет функции для запуска / остановки времени отслеживания. Эти функции запрашивают текущее системное время.
Чтобы сделать этот дизайн тестируемым, я ввел черту TimeService
, которая предоставляет текущую метку времени. У меня есть «реальная» реализация SystemTimeService
, которая возвращает текущее системное время, и поддельная реализация FakeTimeService
для тестов, в которых время может быть установлено извне.
Это текущая реализация:
pub struct Tracker {
// ...
time_service: Box<TimeService>,
}
trait TimeService {
fn time(&self) -> u64;
}
Вот тест, который я хотел бы реализовать:
#[test]
fn tracker_end_when_called_tracks_total_time() {
let (tracker, time_service) = init_tracker();
time_service.set_seconds(0);
tracker.start("feature1");
time_service.set_seconds(10);
tracker.end();
assert_eq!(tracker.total_time, 10);
}
fn init_tracker() -> (Tracker, &FakeTimeService) {
let time_service = Box::new(FakeTimeService::new());
let tracker = Tracker::with_time_service(time_service);
// does not compile, as time service's ownership has been
// moved to the tracker.
(tracker, &*time_service)
}
Проблема в том, что я не знаю, как получить доступ к сервису фальшивого времени из модульного теста, так как трекер владеет им.
Я могу представить несколько решений:
- Используйте
Rc
вместо Box
внутри трекера для совместного владения службой времени
- Сделайте
Tracker
универсальным и добавьте аргумент типа для используемой TimeTracker
реализации
- Добавление времени жизни к функции
init_tracker
Мне не нравится использовать решение 1, так как это не выразило бы идею, что сервис является частью трекера (кажется, нарушает инкапсуляцию).
Решение 2, вероятно, жизнеспособно, но в этом случае мне нужно сделать признак TimeService
и используемые реализации общедоступными, чего я также хотел бы избежать.
Таким образом, наиболее многообещающее решение без изменения дизайна - это время жизни.
Можно ли добавить время жизни к переменным таким образом, чтобы функция init_tracker
могла возвращать трекер и службу поддельного времени?
Есть ли другие решения / лучшие практики?