Skip to content

Testing Strategy

Yasunobu edited this page Jun 26, 2026 · 1 revision

Testing Strategy

Tests use xUnit with coverlet for coverage. The design pays off here: because Core's only side-effect boundary is IInputSynthesizer and time is injected via TimeProvider, the engine is fully testable without touching Windows, and the ViewModel is testable without the WinUI runtime.

Run everything with:

just test     # mise x -- dotnet test KeepPressing.slnx

KeepPressing.Core.Tests — the domain

Tests the engine and helpers against fakes, with deterministic virtual time:

  • FakeInputSynthesizer records an ordered log of Press/Release actions, implements Tap as PressRelease, supports fault injection (ThrowOnTap), and exposes WaitForCountAsync to await a given number of events (timer continuations run on the thread pool after Advance, so you wait for them rather than asserting synchronously).
  • FakeTimeProvider drives PeriodicTimer so repeat timing is exact and instant — no real delays.
  • Coverage focus: the state machine (Idle↔Running), the first immediate press in Repeat (the do/while sends one tap before the first tick), the Up guarantee in Hold (release on cancel and on fault), and VirtualScreen coordinate normalization.

KeepPressing.App.Tests — ViewModel & presentation

Tests MainPageViewModel and SpecBuilder with a set of fakes in Fakes.cs:

Fake Stands in for Purpose
FakeHotkeyListener IHotkeyListener Records registrations, can reject specific VKs (conflict), can raise Pressed
SynchronousUiDispatcher IUiDispatcher Runs marshaled actions inline so tests stay synchronous
FakeCursorLocator ICursorLocator Returns a fixed cursor position
RecordingInputSynthesizer IInputSynthesizer Captures the spec the VM actually built
FakeLocalizer ILocalizer Echoes keys/args so tests need no PRI resources

Coverage focus: hotkey registration and conflict handling (reject a VK → error surfaced, selection reverts and re-registers the previous key), command enablement logic (e.g. start disabled until a key is set), and SpecBuilder validation (NaN interval, missing fixed coordinates, missing key).

Writing a new test

  • Domain behavior → KeepPressing.Core.Tests, with FakeInputSynthesizer + FakeTimeProvider. Use WaitForCountAsync instead of sleeping.
  • UI/translation behavior → KeepPressing.App.Tests, with the Fakes.cs doubles and SynchronousUiDispatcher.
  • Keep timing deterministic — never depend on wall-clock delays.

Related: Architecture · Domain Model & ADTs · Building & Tasks

Clone this wiki locally