Timer
MockTimeSystem.Timer wraps System.Threading.Timer (ITimer). The mocked timer lets you wait for a specific number of executions instead of sleeping.
For the await-based pull loop variant, see PeriodicTimer.
Driving a Timer from a test
MockTimeSystem.Timer.New(...) returns the production ITimer interface, but the underlying instance is always an ITimerMock on a MockTimeSystem - cast it once to gain the testing-only Wait method (or fetch the mock later from timeSystem.TimerHandler[index] if the system under test owns the timer):
MockTimeSystem timeSystem = new();
int ticks = 0;
ITimerMock timer = (ITimerMock)timeSystem.Timer.New(_ => ticks++,
state: null,
dueTime: TimeSpan.FromSeconds(1),
period: TimeSpan.FromSeconds(1));
// Block until the timer fired 3 times (or throw TimeoutException)
timer.Wait(executionCount: 3);
await Expect.That(ticks).IsEqualTo(3);
ITimerMock.Wait(int executionCount = 1, int timeout = 10000, Action<ITimerMock>? callback = null) is the testing-only addition. Note timeout is milliseconds (not TimeSpan - this differs from IAwaitableCallback<T>.Wait, which takes a TimeSpan?). The optional callback runs between executions and receives the timer itself; call Dispose() on it to stop early.
Timer strategies
WithTimerStrategy(ITimerStrategy) controls when timers start and how exceptions inside callbacks are handled:
MockTimeSystem timeSystem = new();
timeSystem.WithTimerStrategy(new TimerStrategy(
mode: TimerMode.StartOnMockWait,
swallowExceptions: true));
TimerMode | Behaviour |
|---|---|
StartImmediately | Default. Timer thread starts as soon as the timer is constructed - matches real Timer. |
StartOnMockWait | Timer is dormant until ITimerMock.Wait(...) is called. Removes thread-scheduling races from the test. |
SwallowExceptions | Behaviour |
|---|---|
false (default) | An exception in the callback throws - your test sees it, mirroring the real-world process crash. |
true | Exceptions are silently dropped, matching legacy code that handled them in unspecified ways. |