Flutter测试完成后如何定位抛出异常的测试用例?
问题背景
运行flutter test时所有测试均通过,但控制台抛出如下异常:
══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════
The following assertion was thrown running a test (but after the test had completed):
Looking up a deactivated widget's ancestor is unsafe.
At this point the state of the widget's element tree is no longer stable.
To safely refer to a widget's ancestor in its dispose() method, save a reference to the ancestor by
calling dependOnInheritedWidgetOfExactType() in the widget's didChangeDependencies() method.When the exception was thrown, this was the stack:
#0 Element._debugCheckStateIsActiveForAncestorLookup.<anonymous closure> (package:flutter/src/widgets/framework.dart:4241:9)
#1 Element._debugCheckStateIsActiveForAncestorLookup (package:flutter/src/widgets/framework.dart:4255:6)
#2 Element.dependOnInheritedWidgetOfExactType (package:flutter/src/widgets/framework.dart:4275:12)
#3 Localizations.of (package:flutter/src/widgets/localizations.dart:472:48)
#4 AppLocalizations.of (package:flutter_gen/gen_l10n/app_localizations.dart:68:26)
#5 ScreenshotCubit._captureWidget (package:photo_editor/ui/screens/editor_screen/bloc/screenshot_cubit/screenshot_cubit.dart:175:51)
<asynchronous suspension>
════════════════════════════════════════════════════════════════════════════════════════════════════
已知异常原因是测试用例在context已失效(未挂载)时尝试获取AppLocalizations字符串,但异常未指明具体测试用例。
更新问题
已通过分组运行缩小至某测试组,但该组批量运行时仍抛出异常(测试全部通过),逐个运行组内用例则无异常。
定位具体测试用例的方法
添加测试名称日志:在每个测试用例的开头和结尾添加打印语句,输出当前测试名称。例如:
test('图片编辑截图测试', () { print('开始运行:图片编辑截图测试'); // 测试逻辑 print('结束运行:图片编辑截图测试'); });运行
flutter test后,查看异常出现前最后打印的测试名称,即可定位到目标测试。利用测试框架钩子:在目标测试组内设置
setUp钩子,自动打印当前运行的测试名称:setUp(() { print('当前测试:${testWidgets.currentTestName}'); });在异常触发点添加日志:修改
ScreenshotCubit._captureWidget方法,在调用AppLocalizations.of前打印当前测试名称(测试环境下可通过testWidgets.currentTestName获取),或者捕获异常并附加测试上下文:Future<void> _captureWidget() async { try { print('当前测试:${testWidgets.currentTestName}'); final localizations = AppLocalizations.of(context); // 后续逻辑 } catch (e) { print('测试${testWidgets.currentTestName}触发异常:$e'); rethrow; } }
解决组运行异常的方法
清理测试间共享状态:检查测试组内是否有全局共享的状态(如Cubit、单例),在
tearDown中确保状态被重置或关闭:tearDown(() { // 关闭并重置ScreenshotCubit final cubit = sl<ScreenshotCubit>(); cubit.close(); sl.resetLazySingleton<ScreenshotCubit>(); });等待异步任务完成:异常发生在测试结束后,可能是异步操作未等待完成就结束测试。确保测试中所有异步任务(如截图操作)都用
await等待:test('批量测试截图', () async { // 触发截图操作 await sl<ScreenshotCubit>().capture(); // 断言逻辑 });禁用并行测试:Flutter默认可能并行运行测试,尝试添加
--no-parallel参数单线程运行测试组:flutter test --no-parallel test/editor_screen_test.dart修复根源问题:按照异常提示,提前缓存
AppLocalizations实例,避免在上下文失效时获取。比如在Widget的didChangeDependencies中保存实例,再传递给Cubit:class EditorScreen extends StatefulWidget { @override _EditorScreenState createState() => _EditorScreenState(); } class _EditorScreenState extends State<EditorScreen> { late AppLocalizations _localizations; @override void didChangeDependencies() { super.didChangeDependencies(); _localizations = AppLocalizations.of(context)!; // 将实例传递给Cubit sl<ScreenshotCubit>().setLocalizations(_localizations); } // ... }后续Cubit直接使用缓存的实例,无需再通过上下文获取。
内容的提问来源于stack exchange,提问作者Haidar

