Skip to main content

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));
TimerModeBehaviour
StartImmediatelyDefault. Timer thread starts as soon as the timer is constructed - matches real Timer.
StartOnMockWaitTimer is dormant until ITimerMock.Wait(...) is called. Removes thread-scheduling races from the test.
SwallowExceptionsBehaviour
false (default)An exception in the callback throws - your test sees it, mirroring the real-world process crash.
trueExceptions are silently dropped, matching legacy code that handled them in unspecified ways.