LoopbackTranscriber.Tests uses xUnit 2.9.3 and targets net10.0-windows with WPF, because the ViewModels depend on WPF types such as DispatcherTimer. Tests run without a window, audio devices, models or worker processes: everything external is replaced by test doubles.
Running
Command
Runs
dotnet test LoopbackTranscriber.slnx -c Release
All the tests.
dotnet test LoopbackTranscriber.slnx --filter Category=Unit
Only the unit tests.
dotnet test LoopbackTranscriber.slnx --filter Category=Integration
Only the integration tests.
dotnet test LoopbackTranscriber.slnx --filter "FullyQualifiedName~RecordingWorkflow"
The tests of one class, by name.
In Visual Studio: Test → Test Explorer → Run All. To group by category, use Group By → Traits.
Layout
Folder or file
Contents
GlobalUsings.cs
Common namespaces and the TranscriptSegment alias.
Infrastructure\
Utilities shared by the tests.
Fakes\
Test doubles for the application's interfaces.
Models\
One file per type in Models\.
Services\
One file per service.
ViewModels\
One file per ViewModel.
Each test class is named after the class it tests plus Tests. Each test follows the Situation_ExpectedResult shape, for example WhileRecording_No_ExitsAtOnce_KeepingTheAudioToProcessLater.
Categories
Each class has a [Trait(TestCategory.Name, …)] trait:
Category
Meaning
Unit
Pure logic: no disk, no clock, no threads. Runs in milliseconds.
Integration
Uses a temporary folder, real WAV files or real timing. Still fast and isolated.
Infrastructure
Type
Used for
TestEnvironment
[ModuleInitializer] that runs before any test. Creates %TEMP%\LoopbackTranscriber.Tests\<pid>, redirects AppLog.LogPath there so nothing is written to the real log, and sets the language to Spanish.
TempDirectory
A temporary folder of each test's own. Deleted when disposed.
TestSettings
AppSettings with every folder inside the temporary one. CreateModelFiles creates fake 1-byte models so the application believes they are installed.
WavFile
Writes and reads test WAVs: tones, silence, PCM or float, deliberately broken headers, duration and format.
Eventually.Until(condition, reason)
Waits without blocking, checking the condition every 10 ms, for up to 5 s by default. If it is not met, fails with the reason.
CollectingProgress<T>
Synchronous IProgress<T> that stores the values.
InterfaceLanguage.Use(lang)
Changes the interface language and restores it when disposed.
InterfaceLanguageTestGroup
xUnit collection without parallelization. The language is global state, so tests that change it go in it.
RecordingRig
Assembles the whole recording flow with doubles: CaptureCoordinator, RecordingWorkflow, RecordingProcessor, RecordingViewModel, the failed recordings store and Teams as the application. Includes helpers such as StartRecordingAsync, WhisperSays and LeaveOrphan.
ShellRig
MainViewModel with all its children, on top of a RecordingRig.
Test doubles
Double
Replaces
What it allows
FakeProcess
IProcessCapture
Writing a test WAV, delaying the start (StartGate), counting starts and setting the peak.
FakeDevice
IDeviceCapture
Returning device tracks with any peak you like.
FakeMic
IMicCapture
Simulating a microphone that starts or not (StartSucceeds).
FakeWhisper
IWhisperTranscriber
Returning segments by file name and a detected language, holding the transcription (Gate) and recording the requests.
FakeAudio
IAudioPreparer
Duration, activity and level maps, and noise gate failures.
FakeSettingsStore
ISettingsStore
In-memory settings. Each read returns a copy, like the real service. Counts updates.
FakeDialogs
IDialogService
Fixed answers to Yes/No and Yes/No/Cancel. Records errors and info messages.
FakeTargetWatcher
ITargetWatcher
Marking applications as open or closed (SetRunning).
FakeCustomApps
ICustomAppsService
Adding and removing applications.
FakeSummarizer
ISummarizer
A fixed summary and a call counter.
FakeLiveHost
IRecordingHost
A simulated recording for the floating window and the tray: delaying or failing the start, and reacting to the stop (OnStop).
FakePageFactory
IPageFactory
Counting the pages created.
FakeModelVerifier
IModelVerifier
Result or exception of the model verification.
Guidelines
No test touches the user's real state: not settings.json, %LocalAppData%, the log, nor real processes or devices.
Services that save to disk take their path through the constructor, such as SettingsService(notices, settingsPath) or FailedRecordingStore(rootFolder). Tests pass them a path inside their TempDirectory.
To wait for something asynchronous, use Eventually.Until, not Task.Delay with a fixed time. To control when an operation finishes, use a TaskCompletionSource (the doubles' Gates).
Messages between ViewModels are tested with a WeakReferenceMessenger of each test's own, not WeakReferenceMessenger.Default.
Expected texts are obtained with Strings.Instance.Get(...) or Format(...) instead of being written by hand, except when the translation itself is what is being tested.
Do not use your real name in test data: the tests use pgomur.