Flutter单元测试用Path Provider和Hive时遇MissingPluginException问题
解决方案
问题根源
- MissingPluginException:单元测试跑在纯Dart环境里,没有Flutter原生平台的支持,
getApplicationDocumentsDirectory依赖原生通道才能工作,所以调用直接失败。 - LateInitializationError:因为Hive初始化失败,
setUp里的todoBox = await Hive.openBox<TodoItemMock>('todoBox')根本没执行,导致用late修饰的todoBox变量没被初始化。
修复步骤
1. 替换Hive初始化目录,摆脱原生插件依赖
把HiveConfig里的getApplicationDocumentsDirectory换成Dart标准库的临时目录,不用再依赖Flutter原生插件:
class HiveConfig { static start() async { // 测试环境直接用系统临时目录,不需要path_provider Directory dir = Directory.systemTemp; Hive.init(dir.path); } }
要是想兼顾生产和测试环境,可以改成支持自定义目录的写法:
class HiveConfig { static start({Directory? customDir}) async { // 测试传临时目录,生产用默认的getApplicationDocumentsDirectory Directory dir = customDir ?? await getApplicationDocumentsDirectory(); Hive.init(dir.path); } } // 测试的setUp里传入临时目录 setUp(() async { await HiveConfig.start(customDir: Directory.systemTemp); // ...其他初始化代码 });
2. 修正测试断言的写法
todoBox.get(0)是同步方法,不需要用completion;而且findsNothing是Widget测试的匹配器,这里应该用isNull:
test('Get no results from the Box', () { expect(todoBox.get(0), isNull); });
3. 确保Mock类和适配器匹配(可选)
你的TodoItemAdapter是给TodoItem写的,但测试用的是TodoItemMock,如果Hive识别不了这个Mock类,要么给TodoItemMock写个适配器,要么直接用真实的TodoItem类测试(如果只是测Hive操作,Mock业务类其实没必要)。要是坚持用Mock,就加个适配器:
class TodoItemMockAdapter extends TypeAdapter<TodoItemMock> { @override final typeId = 0; // 和TodoItemAdapter的typeId保持一致,或者用不同的 @override TodoItemMock read(BinaryReader reader) { // Mock类直接返回实例就行 return TodoItemMock(); } @override void write(BinaryWriter writer, TodoItemMock obj) { // Mock类不需要实际写入逻辑,空实现就行 } } // 在setUp里注册这个适配器 setUp(() async { await HiveConfig.start(customDir: Directory.systemTemp); Hive.registerAdapter(TodoItemMockAdapter()); todoBox = await Hive.openBox<TodoItemMock>('todoBox'); });
4. 让tearDownAll更安全
万一todoBox没初始化成功,关闭前先判断一下:
tearDownAll(() async { if (Hive.isBoxOpen('todoBox')) { await Hive.box('todoBox').close(); } await Hive.close(); });
完整修复后的测试代码示例
import 'dart:io'; import 'package:flutter_test/flutter_test.dart'; import 'package:hive/hive.dart'; import 'package:mocktail/mocktail.dart'; import 'package:todo_app/src/modules/adapter/todo_adapter.dart'; late Box<TodoItemMock> todoBox; class TodoItemMock extends Mock implements TodoItem {} class TodoItemMockAdapter extends TypeAdapter<TodoItemMock> { @override final typeId = 0; @override TodoItemMock read(BinaryReader reader) => TodoItemMock(); @override void write(BinaryWriter writer, TodoItemMock obj) {} } class HiveConfig { static start({Directory? customDir}) async { Directory dir = customDir ?? Directory.systemTemp; Hive.init(dir.path); } } void main() { setUp(() async { await HiveConfig.start(customDir: Directory.systemTemp); Hive.registerAdapter(TodoItemMockAdapter()); todoBox = await Hive.openBox<TodoItemMock>('todoBox'); }); tearDownAll(() async { if (Hive.isBoxOpen('todoBox')) { await Hive.box('todoBox').close(); } await Hive.close(); }); test('Get no results from the Box', () { expect(todoBox.get(0), isNull); }); }
内容的提问来源于stack exchange,提问作者Luiz Fiuza
相关产品推荐
相关产品推荐

