在 Avalonia 中测试 ViewModel 的核心是将其视为普通 C# 类,不依赖 UI 组件,通过验证属性变更通知、命令执行及状态变化来确保逻辑正确,依赖服务需接口注入并用 Moq 等模拟。
在 Avalonia 中对 ViewModel 做单元测试,核心思路是:把 ViewModel 当作普通 C# 类来测,不依赖 UI、不启动窗口、不触发渲染。只要它实现了 INotifyPropertyChanged、用了命令(ICommand)、有业务逻辑或状态流转,就能独立验证。
确保你的 ViewModel 基类已正确支持属性变更通知和命令机制:
ReactiveUI.ViewModel 或 CommunityToolkit.Mvvm.ObservableObject,或手动实现 INotifyPropertyChanged
RelayCommand / ReactiveCommand / AsyncRelayCommand 定义命令,而非直接写事件处理方法Application.Current、Dispatcher.UIThread、DialogHost 等 Avalonia UI 特定对象假设你有如下 ViewModel:
public partial class LoginViewModel : ObservableObject
{
[ObservableProperty]
private string _account;
[ObservableProperty]
private string _pwd;
[ICommand]
public void Login()
{
if (!string.IsNullOrWhiteSpace(Account) && Account.Length > 2)
IsLoggedIn = true;
}
[ObservableProperty]
private bool _isLoggedIn;}
对应测试代码(用 xUnit + Moq):
CommunityToolkit.Mvvm
示例:
[Fact]
public void Login_WhenAccountValid_SetsIsLoggedInToTrue()
{
var vm = new LoginViewModel();
vm.Account = "admin";
vm.Pwd = "123";
vm.Login();
Assert.True(vm.IsLoggedIn);
}
测试属性变更通知(INPC)
关键是要验证 PropertyChanged 事件是否被正确触发:
PropertyChanged 事件,记录触发的属性名CommunityToolkit.Mvvm 的 SetProperty 或 [ObservableProperty],它们默认保障通知逻辑正确简单验证方式:
var vm = new LoginViewModel(); string? raisedProp = null; vm.PropertyChanged += (_, e) => raisedProp = e.PropertyName;vm.Account = "test"; Assert.Equal("Account", raisedProp);
如果用了 ReactiveUI,重点测 ReactiveCommand 的执行、CanExecute 变化、执行结果:
TestScheduler 控制时间流(适合复杂响应式链)Execute 调用后,断言输出属性、服务调用次数、异常等Observable.Return(...) 或 Task.FromResult(...)
例如测试登录失败场景:
var mockAuthService = new Mock(); mockAuthService.Setup(x => x.LoginAsync(It.IsAny (), It.IsAny ())) .ReturnsAsync(false); var vm = new LoginViewModel(mockAuthService.Object); vm.Account = "bad"; vm.Pwd = "pass"; await vm.LoginCommand.ExecuteAsync(null);
Assert.False(vm.IsLoggedIn);
基本上就这些。不复杂但容易忽略的是:别在 ViewModel 里做 UI 导航、弹窗、资源加载——那些该交给 View 或专门的导航服务,否则测试会卡住或失败。