This is a headless Avalonia UI test project for the IHC Lab application. Tests use mocked IHC services (FakeItEasy proxies) and do not require a live IHC controller or settings file.
All IHC API services are mocked using FakeItEasy when the endpoint starts with SpecialEndpoints.MockedPrefix. The mock implementations are defined in utilities/ihc_lab/App/IhcSetup.cs in the IhcFakeSetup class.
Key Classes:
IhcSetup- Main setup class that creates either real or mocked services based on endpointIhcFakeSetup- Static class containing setup methods for each mocked service
To add or modify operations available in mocked services for testing, update the corresponding Setup*Service method in IhcFakeSetup:
public static IAuthenticationService SetupAuthenticationService(IhcSettings settings)
{
var service = A.Fake<IAuthenticationService>();
// Configure mock behavior for operations (note: async methods require Task.FromResult)
A.CallTo(() => service.Authenticate()).Returns(Task.FromResult(new IhcUser { ... }));
A.CallTo(() => service.Authenticate(A<string>._, A<string>._, A<Application>._))
.ReturnsLazily((string username, string password, Application app) =>
Task.FromResult(new IhcUser { ... }));
return service;
}Example: Adding a new operation to AuthenticationService
// For async methods returning Task<T>
A.CallTo(() => service.Logout()).Returns(Task.FromResult(true));
// For async methods returning Task (void)
A.CallTo(() => service.ClearCache()).Returns(Task.CompletedTask);IAuthenticationService - Fully mocked with user authentication operations
Authenticate()- Returns mock user with credentials from settingsPing(),Disconnect(),IsAuthenticated()- Return success values
IControllerService - Comprehensively mocked with all 24 operations
- Project operations:
GetProject(),StoreProject(),GetProjectInfo(),IsIHCProjectAvailable() - State operations:
GetControllerState(),WaitForControllerStateChange(),EnterProjectChangeMode(),ExitProjectChangeMode() - SD card operations:
IsSDCardReady(),GetSDCardInfo() - Backup operations:
GetBackup(),Restore() - Segmentation operations:
GetIHCProjectSegment(),StoreIHCProjectSegment(),GetIHCProjectSegmentationSize(),GetIHCProjectNumberOfSegments() - S0 energy meter operations:
GetS0MeterValue(),ResetS0Values(),SetS0Consumption(),SetS0FiscalYearStart()
Other Services - Empty fakes (no operations configured)
IResourceInteractionService,IConfigurationService,IOpenAPIService,INotificationManagerService,IMessageControlLogService,IModuleService,ITimeManagerService,IUserManagerService,IAirlinkManagementService,ISmsModemService,InternalTestService
-
Setup.RunBeforeAnyTests()(TestSetup.cs) - Runs once before all tests:- Configures
Program.configwith mocked endpoint (SpecialEndpoints.MockedPrefix) - Initializes logger factory with
TestContextLoggerProviderfor test output visibility
- Configures
-
TestAppBuilder.BuildAvaloniaApp()(TestSetup.cs) - Creates Avalonia test application:- Configures headless Avalonia with Skia renderer (enables screenshot capture)
- Sets up logging to forward to NUnit TestContext
-
Test Execution:
[AvaloniaTest]attribute creates headless UI session[CaptureScreenshotOnFailure]captures PNG on test failure- Tests create
MainWindowwhich instantiatesIhcSetup IhcSetupdetects mocked endpoint and usesIhcFakeSetupto create services
Base Class: AvaloniaTestBase
SetupMainWindowAsync()- Creates, initializes, and shows MainWindowCaptureScreenshotOnFailure()- Called automatically on test failure
Logging Suppression: SuppressLogging
- Use
using (new SuppressLogging()) { }to suppress logs in tests that intentionally trigger errors/warnings
# Run all lab tests
dotnet test tests/safe_lab_tests/safe_lab_tests.csproj
# Run specific test class
dotnet test tests/safe_lab_tests/safe_lab_tests.csproj --filter "FullyQualifiedName~ModelTests"
# Run with verbose output
dotnet test tests/safe_lab_tests/safe_lab_tests.csproj --verbosity detailed- ViewModel property change notifications
- LabAppService event subscription and handling
- Collection synchronization (Services, Operations)
- Circular update prevention mechanism
- Parameter control setup and lifecycle
- Bidirectional synchronization (GUI ↔ LabAppService)
- Value persistence across operation switches
- Event subscription for DynField controls
- Real-time GUI → LabAppService synchronization
- Real-time LabAppService → GUI synchronization
- Complex parameter reconstruction
- Radio button independence for bool parameters
- Circular update prevention
- Basic window lifecycle and initialization
- Service and operation ComboBox population
- Run button functionality