11 releases

new 0.5.3 Mar 7, 2025
0.5.1 Jun 2, 2024
0.3.2 Feb 16, 2024
0.3.1 Jun 1, 2023
0.1.1 Jul 12, 2020

#33 in Testing

Download history 14412/week @ 2024-11-21 16144/week @ 2024-11-28 17632/week @ 2024-12-05 20820/week @ 2024-12-12 10733/week @ 2024-12-19 6436/week @ 2024-12-26 14554/week @ 2025-01-02 40117/week @ 2025-01-09 23827/week @ 2025-01-16 20081/week @ 2025-01-23 19387/week @ 2025-01-30 22644/week @ 2025-02-06 20552/week @ 2025-02-13 22383/week @ 2025-02-20 23191/week @ 2025-02-27 26523/week @ 2025-03-06

95,973 downloads per month
Used in 42 crates (24 directly)

0BSD license

31KB
501 lines

mock_instant

Modules

NOTE As of version 0.5. MockClock/Instant/SystemTime have been moved to specific modules

NOTE The modules, global and thread_local change the behavior across threads. If global is used, the clock keeps its state across threads, otherwise if thread_local is used, a new source is made for each thread. In most cases, especially when unit testing, thread_local will deliver the most expected behavior.

To ensure unsurprising behavior, reset the clock before each test (if that behavior is applicable.)

Basics

This crate allows you to test Instant/Duration/SystemTime code, deterministically.

It provides a replacement std::time::Instant that uses a deterministic 'clock'

You can swap out the std::time::Instant with this one by doing something similar to:

#[cfg(test)]
use mock_instant::thread_local::Instant;

#[cfg(not(test))]
use std::time::Instant;

or for a std::time::SystemTime

#[cfg(test)]
use mock_instant::thread_local::{SystemTime, SystemTimeError};

#[cfg(not(test))]
use std::time::{SystemTime, SystemTimeError};
use mock_instant::thread_local::{MockClock, Instant};
use std::time::Duration;

let now = Instant::now();
MockClock::advance(Duration::from_secs(15));
MockClock::advance(Duration::from_secs(2));

// its been '17' seconds
assert_eq!(now.elapsed(), Duration::from_secs(17));

API

// Overrides the current time to this `Duration`
MockClock::set_time(time: Duration)

// Advance the current time by this `Duration`
MockClock::advance(time: Duration)

// Get the current time
MockClock::time() -> Duration

// Overrides the current `SystemTime` to this duration
MockClock::set_system_time(time: Duration)

// Advance the current `SystemTime` by this duration
MockClock::advance_system_time(time: Duration)

// Get the current `SystemTime`
MockClock::system_time() -> Duration

// Determine if this MockClock was thread-local: (useful for assertions to ensure the right mode is being used)
MockClock::is_thread_local() -> bool
Instant::now().is_thread_local() -> bool
SystemTime::now().is_thread_local() -> bool

Usage

NOTE The clock starts at Duration::ZERO

In your tests, you can use MockClock::set_time(Duration::ZERO) to reset the clock back to 0. Or, you can set it to some sentinel time value.

Then, before you check your time-based logic, you can advance the clock by some Duration (it'll freeze the time to that duration)

You can also get the current frozen time with MockClock::time

SystemTime is also mockable with a similar API.

Thread-safety

Two modes are provided via modules. The APIs are identical but the MockClock source has different behavior in different threads.

mock_instant::thread_local

  • MockClock will have an independent state per thread
  • Instantwill have an independent state per thread
  • SystemTime will have an independent state per thread

Using thread_local creates a shared clock in each thread of operation within the test application. It is recommended for unit tests and integration tests that test single-threaded behavior.

thread_local will be the module used in most cases and will create the least surprise.

mock_instant::global

  • MockClock will have shared state per thread
  • Instantwill have shared state per thread
  • SystemTime will have shared state per thread

Using global creates a shared clock across all thread of operation within the test application. It is recommended for integration tests that are specifically testing multi-threaded behavior. Using global when not testing multi-threaded behavior is likely to cause inconsistent and confusing errors.

Rust's unit test test-runner executes unit tests in multiple threads (unless directed to run single-threaded). If multiple unit tests are running simultaneouly within the test execution, changes to the global clock (resetting or advancing the clock) in one test can created unexpected results in other tests that are executing simultaneously, by changing the clock when the tests assume the clock is not being changed.

License

License: 0BSD

No runtime deps