Dart单元测试中如何模拟File实例覆写writeAsBytes方法
Dart单元测试模拟
File.writeAsBytes的实现方案 核心实现思路
当前业务代码直接硬编码File(path)构造真实文件对象,默认会关联系统IO逻辑。要实现不真实写文件、仅拦截方法获取路径的目标,核心是解耦File对象的创建逻辑,让测试环节可以自由替换成无IO的mock实现。
业务代码改造(低侵入方案)
在ShareHelper中增加可替换的文件构造入口,默认走真实的File创建逻辑,测试时可以临时替换:
import 'dart:io'; import 'dart:convert'; import 'path_provider/path_provider.dart'; class ShareHelper { // 可覆写的文件构造函数,默认使用真实File实现 static File Function(String path) fileCreator = (path) => File(path); static Future<List<String>> createFilesFromString( List<FileShare> files) async => Future.wait( files.map((fileShare) => _createFileFromString(fileShare)).toList()); static Future<String> _createFileFromString(FileShare fileShare) async { final str = fileShare.base64.split(','); Uint8List bytes = base64.decode(str[1]); String dir = (await getTemporaryDirectory()).path; final fileName = _createFileName(fileShare.name); final fullPath = "$dir/$fileName"; // 替换原有硬编码的File构造,使用可注入的fileCreator final file = fileCreator(fullPath); await file.writeAsBytes(bytes); return file.path; } // 测试结束后重置默认实现,避免跨用例污染 static void reset() { fileCreator = (path) => File(path); } // 原有_createFileName逻辑保持不变 static String _createFileName(String originName) { // 原有实现 } }
无IO的MockFile实现
自定义Mock类继承自系统File,完全覆写writeAsBytes方法,不触发任何磁盘写入:
class MockFile extends File { MockFile(super.path); @override Future<File> writeAsBytes( Uint8List bytes, { FileMode mode = FileMode.write, bool flush = false, }) async { // 不调用父类的真实IO逻辑,无任何文件写入行为 return this; } }
如果不想维护自定义MockFile类,也可以使用mocktail、mockito这类第三方mock框架生成File的mock实例,同样覆写writeAsBytes方法即可,实现效果一致。
单元测试实现
测试环节替换文件构造逻辑,记录所有生成的文件路径,完全不需要真实保存文件:
test('应正确返回所有待分享文件的生成路径,不触发真实文件写入', () async { // 保留原有路径mock逻辑 PathProviderPlatform.instance = MockPathProviderPlatform(); // 记录所有生成的文件路径 final recordedPaths = <String>[]; // 临时替换文件构造逻辑 ShareHelper.fileCreator = (path) { recordedPaths.add(path); return MockFile(path); }; final files = [ FileShare( name: 'Teste/A', base64: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUg...', ), ]; final paths = await ShareHelper.createFilesFromString(files); // 校验返回的路径与记录的路径完全一致 expect(paths, recordedPaths); // 测试完成后重置全局静态状态,避免影响其他测试用例 ShareHelper.reset(); });
关键注意事项
- 整个流程没有任何磁盘IO操作,
writeAsBytes的调用被完全拦截 - 可以直接通过记录的路径列表完成业务逻辑校验,不需要读取真实磁盘上的文件
- 全局静态的
fileCreator入口一定要在用例结束后重置,否则会造成测试状态的跨用例污染
内容的提问来源于stack exchange,提问作者Gabriel Ferreira
相关产品推荐
相关产品推荐

