Core Principles
1. The Fundamental Problem of File System Dependencies
Code that traditionally directly uses System.IO static classes is difficult to test, for reasons including:
- Speed Issues: Actual disk IO is 10-100 times slower than memory operations
- Environment Dependency: Test results are affected by file system state, permissions, and paths
- Side Effects: Tests leave traces on disk, affecting other tests
- Concurrency Issues: Multiple tests operating on the same file simultaneously create race conditions
- Difficulty Simulating Errors: Difficult to simulate exceptions such as insufficient permissions or insufficient disk space
2. System.IO.Abstractions Solution
A package that wraps System.IO static classes into interfaces, supporting dependency injection and test doubles.
Required NuGet Packages:
<!-- 正式環境 -->
<PackageReference Include="System.IO.Abstractions" Version="22.1.0" />
<!-- 測試專案 -->
<PackageReference Include="System.IO.Abstractions.TestingHelpers" Version="22.1.0" />
3. Refactoring Steps
Step One: Change code that directly uses static classes to depend on IFileSystem
// ❌ 重構前(不可測試)
public class ConfigService
{
public string LoadConfig(string path) => File.ReadAllText(path);
}
// ✅ 重構後(可測試)
public class ConfigService
{
private readonly IFileSystem _fileSystem;
public ConfigService(IFileSystem fileSystem) => _fileSystem = fileSystem;
public string LoadConfig(string path) => _fileSystem.File.ReadAllText(path);
}
Step Two: Register the real implementation in the DI container
services.AddSingleton<IFileSystem, FileSystem>();
Step Three: Use MockFileSystem in tests
var mockFs = new MockFileSystem(new Dictionary<string, MockFileData>
{
["config.json"] = new MockFileData("{ \"key\": \"value\" }")
});
var service = new ConfigService(mockFs);
MockFileSystem Testing Patterns
Covers four core testing patterns: default file state setup, verifying write results, directory operation testing, and using NSubstitute to simulate IO exceptions (UnauthorizedAccessException, etc.). Also includes advanced techniques: stream operation testing, file information query testing, and backup file testing.
For complete MockFileSystem testing patterns and advanced techniques, refer to references/mockfilesystem-patterns.md
Best Practices
Should Do
- Use Path.Combine to handle paths —
_fileSystem.Path.Combine("configs", "app.json") - Defensively check file existence — check before reading
_fileSystem.File.Exists() - Automatically create necessary directories — ensure the directory exists before writing
- Properly handle various IO exceptions — UnauthorizedAccessException、IOException、DirectoryNotFoundException
- Use an independent MockFileSystem for each test — ensure test isolation
Should Avoid
- Hardcoding path separators — use
Path.Combineinstead of\\or/ - Using the real file system in unit tests — use MockFileSystem
- Ignoring exception handling — do not assume files definitely exist
Performance Considerations
- MockFileSystem Speed: 10-100 times faster than real file operations
- Memory Usage: Only create files necessary for the test, avoid simulating extremely large files
Practical Integration Examples
Please refer to templates/ for complete implementations under the directory:
configmanager-service.cs- Configuration file management service (load/save/backup)filemanager-service.cs- File management service (copy/directory operations/error handling)
Output Format
- Generate a service class that uses the IFileSystem interface
- Generate a test class that uses MockFileSystem
- Include test examples for file reading and writing, directory operations, and path handling
- Provide .csproj package references (System.IO.Abstractions, System.IO.Abstractions.TestingHelpers)
Reference Resources
Original Article
The content of this skill is distilled from the "Old-School Software Engineer's Testing Practice - 30-Day Challenge" article series:
- Day 17 - File and IO Testing: Using System.IO.Abstractions to Mock the File System
Official Documentation
Related Skills
nsubstitute-mocking- Test Doubles and Mockingunit-test-fundamentals- Unit Testing Fundamentals