基于 SOC 职业分类
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/tomevault-io/skills-registry --skill maui-unit-testing命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
正在显示 SKILL.md
| Use when this capability is needed.
> Use when this capability is needed.
Review architecture and API design for the vfs-s3 project. Use when the user mentions @architect, asks to review an issue's design, discuss module boundaries, API shape, or architectural decisions for vfs-s3. Also trigger when the user wants to create an ADR (Architecture Decision Record) or evaluate a technical approach for the project. Intended for dispatch from Codex automation or Claude routines; GitHub trigger phrase: @vfs-s3-bot please prepare design doc Use when this capability is needed.
| name | maui-unit-testing |
| description | > Use when this capability is needed. |
For project templates, xUnit examples, ViewModel test patterns, and CLI commands, see references/unit-testing-api.md.
<!-- ❌ xUnit can't run on platform-specific TFMs -->
<TargetFramework>net9.0-ios</TargetFramework>
<!-- ✅ Use plain .NET TFM for desktop test host -->
<TargetFrameworks>net9.0;net10.0</TargetFrameworks>
If your test project references the app project, the app tries to build as Exe for the test TFM — this fails. Add a conditional OutputType:
<!-- ❌ App .csproj without conditional — breaks test builds -->
<OutputType>Exe</OutputType>
<!-- ✅ Library for test TFM, Exe for platform TFMs -->
<PropertyGroup Condition="'$(TargetFramework)' == 'net9.0'">
<OutputType>Library</OutputType>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)' != 'net9.0'">
<OutputType>Exe</OutputType>
</PropertyGroup>
ViewModels that directly call static MAUI APIs are untestable:
// ❌ Untestable — Shell.Current requires a running MAUI app
public async Task GoToDetail(int id)
=> await Shell.Current.GoToAsync($"detail?id={id}");
// ✅ Inject an interface — fully testable
public class MyViewModel(INavigationService nav)
{
public async Task GoToDetail(int id)
=> await nav.GoToAsync($"detail?id={id}");
}
Static APIs to wrap behind interfaces:
Shell.Current → INavigationServiceApplication.Current → avoid entirelySecureStorage.Default → ISecureStorageConnectivity.Current → IConnectivity// ❌ Testing the binding — fragile, needs a running UI
Assert.Equal("Hello", label.Text);
// ✅ Testing the ViewModel — fast, no platform dependency
Assert.Equal("Hello", viewModel.Title);
Assert.True(viewModel.SaveCommand.CanExecute(null));
| MAUI Service | Mock Strategy |
|---|---|
ISecureStorage | Mock<ISecureStorage> — stub GetAsync/SetAsync |
IPreferences | Mock<IPreferences> — stub Get/Set/Remove |
IConnectivity | Mock<IConnectivity> — return NetworkAccess |
IGeolocation | Mock<IGeolocation> — return fixed Location |
IFilePicker | Mock<IFilePicker> — return FileResult |
IMediaPicker | Mock<IMediaPicker> — return FileResult |
| Shell navigation | Abstract behind INavigationService |
IDispatcher | Stub Dispatch to invoke action synchronously |
Define service interfaces so ViewModels have zero MAUI platform dependencies:
// ✅ These make your entire ViewModel layer testable
public interface INavigationService
{
Task GoToAsync(string route);
Task GoBackAsync();
}
public interface IDialogService
{
Task<bool> ConfirmAsync(string title, string message);
}
Register implementations in MauiProgram.cs; inject interfaces into ViewModels.
Application.Current or Shell.Current in ViewModels — wrap in injectable servicesObservableCollection<T> and [ObservableProperty] (MVVM Toolkit) for testable stateCanExecute, not UI bindingsTaskCompletionSource to test async waiting flowsdotnet test in CI to catch regressions earlyxunit.runner.devices for real platform APIs (sensors, camera, Bluetooth)net9.0/net10.0 (not platform-specific TFMs)OutputType for test TFMIDispatcher mocked to invoke synchronously in testsdotnet test runs green in CIConverted and distributed by TomeVault — claim your Tome and manage your conversions.