如何正确Mock IFileSystem与IFileSystemWatcher?文件删除时OnChanged未触发
解决Mock环境下FileSystemWatcher事件不触发的问题
问题根源分析
- 错误Mock了
TestFileWatcher本身:要测试的是这个类的真实逻辑,不该Mock它,Mock对象应该是它的依赖组件。 MockFileSystem默认的FileSystemWatcherFactory无自动事件触发逻辑:模拟文件系统的操作(比如删除)不会主动通知Watcher,需要手动触发事件。OnChanged方法依赖真实文件系统:直接实例化FileInfo会绕过注入的IFileSystem,在Mock环境中引发不一致问题。
修复步骤
1. 修正TestFileWatcher的文件系统依赖逻辑
把OnChanged里的真实FileInfo替换为IFileSystem提供的实例,保持依赖注入一致性:
private void OnChanged(object source, FileSystemEventArgs e) { // 使用注入的IFileSystem获取FileInfo,避免直接访问真实文件系统 var fi = _fileSystem.FileInfo.FromFileName(e.FullPath); fi.Refresh(); isDeleted = true; }
2. 调整测试代码:移除TestFileWatcher的Mock,手动触发事件
直接实例化TestFileWatcher,手动模拟FileSystemWatcher的事件触发:
[Fact] public void Mock_TestFileWatcher() { var mockFileSystem = new MockFileSystem(); mockFileSystem.AddFile("deleteFile", new MockFileData("")); // 直接实例化要测试的TestFileWatcher var testWatcher = new TestFileWatcher(mockFileSystem); // 获取TestFileWatcher使用的FileSystemWatcher实例(或模拟对应实例) var watcher = mockFileSystem.FileSystemWatcher.CreateNew(); watcher.Path = mockFileSystem.Path.GetDirectoryName("deleteFile"); watcher.Filter = mockFileSystem.Path.GetFileName("deleteFile"); // 手动触发Deleted事件,模拟文件被删除的场景 watcher.Deleted?.Invoke(null, new FileSystemEventArgs( WatcherChangeTypes.Deleted, mockFileSystem.Path.GetDirectoryName("deleteFile"), mockFileSystem.Path.GetFileName("deleteFile") )); Assert.True(testWatcher.isDeleted); }
3. 更灵活的方案:用Moq精准Mock IFileSystemWatcher
通过Moq直接MockIFileSystemWatcher,并注入到TestFileWatcher中,完全控制事件触发:
[Fact] public void Mock_TestFileWatcher_WithMoq() { // Mock IFileSystemWatcher var mockWatcher = new Mock<IFileSystemWatcher>(); // Mock IFileSystem,指定FileSystemWatcher的返回值 var mockFileSystem = new Mock<IFileSystem>(); mockFileSystem.Setup(fs => fs.FileSystemWatcher.CreateNew()).Returns(mockWatcher.Object); mockFileSystem.Setup(fs => fs.Path.GetDirectoryName("deleteFile")).Returns(""); mockFileSystem.Setup(fs => fs.Path.GetFileName("deleteFile")).Returns("deleteFile"); // 实例化TestFileWatcher var testWatcher = new TestFileWatcher(mockFileSystem.Object); // 手动触发Deleted事件 mockWatcher.Raise(w => w.Deleted += null, new FileSystemEventArgs(WatcherChangeTypes.Deleted, "", "deleteFile")); Assert.True(testWatcher.isDeleted); }
关键注意事项
- 不要Mock待测试的类:测试的核心是验证
TestFileWatcher的真实逻辑,Mock的应该是它的依赖(如IFileSystem、IFileSystemWatcher)。 - MockFileSystem的Watcher不会自动触发事件:模拟环境没有真实文件系统的事件通知机制,必须手动触发事件来模拟业务场景。
- 所有文件操作通过注入的IFileSystem完成:避免直接使用
System.IO下的类,确保测试完全在模拟环境中运行。
内容的提问来源于stack exchange,提问作者user2384330
相关产品推荐
相关产品推荐

