异步方法单元测试:如何触发事件并验证MyViewModel的InfoList填充?
测试MyViewModel时避免重复InfoLoader配置的方案
你不需要重复配置真实的InfoLoader,因为MyViewModel的测试核心是验证它正确响应IInfoLoader的事件并填充InfoList,而非关心IInfoLoader内部如何读取文件生成数据。直接MockIInfoLoader接口即可,完全隔离对FileSystem的依赖,同时避免重复代码。
具体实现步骤
- 准备预期的测试数据:创建你期望最终出现在
InfoList中的MyInfo集合。 - Mock
IInfoLoader:- 配置
LoadInfoAsync方法返回已完成的Task。 - 在
LoadInfoAsync被调用时,手动触发InfoLoaded事件,将预期的MyInfo逐个传入。
- 配置
- 实例化
MyViewModel并执行测试逻辑,最后验证InfoList与预期一致。
完整测试代码
[TestMethod] public async Task MyViewModel_StartLoadInfoAsync_Success() { // 1. 准备预期数据 var expectedInfoList = new List<MyInfo> { new MyInfo("name1"), new MyInfo("name2") }; // 2. Mock IInfoLoader var mockInfoLoader = new Mock<IInfoLoader>(); // 配置LoadInfoAsync:当方法被调用时,触发InfoLoaded事件传入所有预期的MyInfo mockInfoLoader.Setup(loader => loader.LoadInfoAsync()) .Returns(() => { foreach (var info in expectedInfoList) { mockInfoLoader.Raise(loader => loader.InfoLoaded += null, info); } return Task.CompletedTask; }); // 3. 执行测试逻辑 var vm = new MyViewModel(mockInfoLoader.Object); await vm.StartLoadInfoAsync(); // 4. 验证结果:将ObservableCollection转为List后对比 var actualInfoList = vm.InfoList.ToList(); CollectionAssert.AreEqual(expectedInfoList, actualInfoList); }
为什么这么做?
- 关注点分离:
InfoLoader的逻辑已经在单独的测试中验证过,ViewModel测试只需要确保它正确处理IInfoLoader的事件和方法调用。 - 避免重复配置:不需要再重复创建
FileSystem的Mock、设置文件读取逻辑,大幅简化测试代码。 - 隔离依赖:完全隔离了文件系统等外部依赖,测试更稳定、执行更快。
额外注意事项
- 如果
MyInfo没有重写Equals和GetHashCode,CollectionAssert.AreEqual会比较引用而非值,这时需要自定义比较器,或者逐个验证属性是否一致。 - 使用Moq的
Raise方法触发事件时,要注意事件委托的签名必须匹配(这里是Action<MyInfo>,所以传入单个MyInfo参数即可)。
内容的提问来源于stack exchange,提问作者theateist
相关产品推荐
相关产品推荐

