diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/Raw/FakeRawTreeService.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/Raw/FakeRawTreeService.cs
new file mode 100644
index 00000000..9c11df75
--- /dev/null
+++ b/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/Raw/FakeRawTreeService.cs
@@ -0,0 +1,127 @@
+using ZB.MOM.WW.OtOpcUa.AdminUI.Uns;
+
+namespace ZB.MOM.WW.OtOpcUa.AdminUI.Tests.Raw;
+
+///
+/// Minimal fake for component-class tests (AdminUI has no bUnit, so the
+/// raw modals are exercised as plain classes with this injected). Only the raw modals' open-path
+/// members — and — are
+/// configurable, via the two settable delegates; every other member throws, so an unexpected call
+/// fails the test instead of passing vacuously. The two delegates themselves default to throwing for
+/// the same reason: a test must opt in to the calls it expects.
+///
+internal sealed class FakeRawTreeService : IRawTreeService
+{
+ /// The stand-in; throws until a test sets it.
+ public Func OnLoadTagForEdit { get; set; } =
+ _ => throw new NotImplementedException();
+
+ /// The stand-in; throws until a test sets it.
+ public Func OnLoadMergedProbeConfig { get; set; } =
+ _ => throw new NotImplementedException();
+
+ ///
+ public Task LoadTagForEditAsync(string tagId, CancellationToken ct = default)
+ => Task.FromResult(OnLoadTagForEdit(tagId));
+
+ ///
+ public Task<(string DriverType, string MergedConfigJson)?> LoadMergedProbeConfigAsync(
+ string deviceId, CancellationToken ct = default)
+ => Task.FromResult(OnLoadMergedProbeConfig(deviceId));
+
+ ///
+ public Task> LoadRootsAsync(CancellationToken ct = default)
+ => throw new NotImplementedException();
+
+ ///
+ public Task> LoadChildrenAsync(RawNode parent, CancellationToken ct = default)
+ => throw new NotImplementedException();
+
+ ///
+ public Task CreateFolderAsync(string clusterId, string? parentFolderId, string name, CancellationToken ct = default)
+ => throw new NotImplementedException();
+
+ ///
+ public Task RenameFolderAsync(string rawFolderId, string newName, byte[] rowVersion, CancellationToken ct = default)
+ => throw new NotImplementedException();
+
+ ///
+ public Task DeleteFolderAsync(string rawFolderId, byte[] rowVersion, CancellationToken ct = default)
+ => throw new NotImplementedException();
+
+ ///
+ public Task MoveDriverToFolderAsync(string driverInstanceId, string? targetFolderId, byte[] rowVersion, CancellationToken ct = default)
+ => throw new NotImplementedException();
+
+ ///
+ public Task CreateDriverAsync(string clusterId, string? folderId, string name, string driverType, string driverConfigJson, CancellationToken ct = default)
+ => throw new NotImplementedException();
+
+ ///
+ public Task RenameDriverAsync(string driverInstanceId, string newName, byte[] rowVersion, CancellationToken ct = default)
+ => throw new NotImplementedException();
+
+ ///
+ public Task SetDriverEnabledAsync(string driverInstanceId, bool enabled, byte[] rowVersion, CancellationToken ct = default)
+ => throw new NotImplementedException();
+
+ ///
+ public Task DeleteDriverAsync(string driverInstanceId, byte[] rowVersion, CancellationToken ct = default)
+ => throw new NotImplementedException();
+
+ ///
+ public Task CreateDeviceAsync(string driverInstanceId, string name, string deviceConfigJson, CancellationToken ct = default)
+ => throw new NotImplementedException();
+
+ ///
+ public Task RenameDeviceAsync(string deviceId, string newName, byte[] rowVersion, CancellationToken ct = default)
+ => throw new NotImplementedException();
+
+ ///
+ public Task DeleteDeviceAsync(string deviceId, byte[] rowVersion, CancellationToken ct = default)
+ => throw new NotImplementedException();
+
+ ///
+ public Task CreateTagGroupAsync(string deviceId, string? parentGroupId, string name, CancellationToken ct = default)
+ => throw new NotImplementedException();
+
+ ///
+ public Task RenameTagGroupAsync(string tagGroupId, string newName, byte[] rowVersion, CancellationToken ct = default)
+ => throw new NotImplementedException();
+
+ ///
+ public Task DeleteTagGroupAsync(string tagGroupId, byte[] rowVersion, CancellationToken ct = default)
+ => throw new NotImplementedException();
+
+ ///
+ public Task DeleteTagAsync(string tagId, byte[] rowVersion, CancellationToken ct = default)
+ => throw new NotImplementedException();
+
+ ///
+ public Task CreateTagAsync(string deviceId, string? tagGroupId, RawTagInput input, CancellationToken ct = default)
+ => throw new NotImplementedException();
+
+ ///
+ public Task UpdateTagAsync(string tagId, RawTagInput input, byte[] rowVersion, CancellationToken ct = default)
+ => throw new NotImplementedException();
+
+ ///
+ public Task ImportTagsAsync(string deviceId, IReadOnlyList rows, CancellationToken ct = default)
+ => throw new NotImplementedException();
+
+ ///
+ public Task LoadDriverForEditAsync(string driverInstanceId, CancellationToken ct = default)
+ => throw new NotImplementedException();
+
+ ///
+ public Task UpdateDriverAsync(string driverInstanceId, string name, string driverConfigJson, string? resilienceConfig, byte[] rowVersion, CancellationToken ct = default)
+ => throw new NotImplementedException();
+
+ ///
+ public Task LoadDeviceForEditAsync(string deviceId, CancellationToken ct = default)
+ => throw new NotImplementedException();
+
+ ///
+ public Task UpdateDeviceAsync(string deviceId, string name, string deviceConfigJson, bool enabled, byte[] rowVersion, CancellationToken ct = default)
+ => throw new NotImplementedException();
+}