You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Floor数据库的Widget真实异步测试方案咨询

问题:测试真实异步数据库操作时,Provider异步方法执行滞后导致测试失败

在使用Floor内存数据库测试Widget时,所有数据操作为真实异步操作,但测试中fetchGroupedSetsByExercise总是在测试断言完成后才执行完毕,导致断言找不到"Clear"按钮失败。测试输出显示FETCH COMPLETED在测试异常之后才打印。

测试代码

testWidgets('test add new set', (WidgetTester tester) async {
  await pumpApp(tester, exercisesProvider, setRepository, exerciseRepository);
  await tester.runAsync(() async {
    var bench = find.text('Bench Press');
    expect(bench, findsOneWidget);
    await tester.tap(bench);
    await tester.pump();
    await tester.pump();
    // Should be found if content is loaded
    expect(find.text('Clear'), findsOneWidget);
  });
});

被测Widget关键代码

@override
Widget build(BuildContext context) {
  // 此异步方法执行时机滞后
  Provider.of<SetProvider>(context, listen: false)
      .fetchGroupedSetsByExercise(widget.selectedExercise.id!);

  return Column(
    children: [
      Expanded(
        child: Consumer<SetProvider>(builder: (context, setProvider, child) {
          final groupedSets = setProvider.groupedSets;
          if (setProvider.groupedSetsState == IOState.loading ||
              setProvider.groupedSetsState == IOState.initial) {
            return ...; // 加载中UI
          } else if (setProvider.groupedSetsState == IOState.error) {
            return ...; // 错误UI
          } else if (groupedSets.isEmpty) {
            return ...; // 空数据UI
          }
          // 包含Clear按钮的列表
          return buildSetList(groupedSets, setProvider);
        }),
      ),
    ],
  );
}

Provider关键代码

Future<void> fetchGroupedSetsByExercise(int exerciseID,
    {bool? reverse}) async {
  groupedSets = groupSetsByDate(
      await fetchSetsByExercise(exerciseID, reverse: reverse));
  lastSetEntry = groupedSets.values.lastOrNull?.lastOrNull;
  groupedSetsState = IOState.success;
  notifyListeners();
  print("FETCH COMPLETED");
}

解决方案

1. 手动等待Provider的异步任务完成

在测试中直接获取Provider实例,手动调用并等待异步方法完成后再执行断言:

testWidgets('test add new set', (WidgetTester tester) async {
  await pumpApp(tester, exercisesProvider, setRepository, exerciseRepository);
  
  var bench = find.text('Bench Press');
  expect(bench, findsOneWidget);
  await tester.tap(bench);
  await tester.pump();

  // 获取当前Widget对应的SetProvider实例
  final setProvider = Provider.of<SetProvider>(
    tester.element(find.byType(YourTargetWidget)), 
    listen: false
  );
  // 等待异步加载完成
  await setProvider.fetchGroupedSetsByExercise(widget.selectedExercise.id!);
  // 触发Widget重建
  await tester.pump();

  expect(find.text('Clear'), findsOneWidget);
});

2. 结合pumpAndSettle与状态监听

利用pumpAndSettle处理所有待处理帧,同时循环等待Provider状态变为成功:

testWidgets('test add new set', (WidgetTester tester) async {
  await pumpApp(tester, exercisesProvider, setRepository, exerciseRepository);
  
  var bench = find.text('Bench Press');
  expect(bench, findsOneWidget);
  await tester.tap(bench);
  await tester.pump();

  final setProvider = Provider.of<SetProvider>(
    tester.element(find.byType(YourTargetWidget)), 
    listen: false
  );
  // 循环等待状态变为success,同时处理帧
  while (setProvider.groupedSetsState != IOState.success) {
    await tester.pump(const Duration(milliseconds: 100));
  }
  // 或者直接用pumpAndSettle等待所有异步操作完成
  // await tester.pumpAndSettle(const Duration(seconds: 1));

  expect(find.text('Clear'), findsOneWidget);
});

3. 优化Widget中异步方法的触发时机(可选)

避免在build方法中直接调用异步方法(build可能被频繁触发),改用initState结合addPostFrameCallback确保只执行一次:

class YourTargetWidget extends StatefulWidget {
  // ... 原Widget参数
}

class _YourTargetWidgetState extends State<YourTargetWidget> {
  @override
  void initState() {
    super.initState();
    // 在第一帧绘制完成后触发异步加载
    WidgetsBinding.instance.addPostFrameCallback((_) {
      Provider.of<SetProvider>(context, listen: false)
          .fetchGroupedSetsByExercise(widget.selectedExercise.id!);
    });
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      // ... 原Widget内容
    );
  }
}

内容的提问来源于stack exchange,提问作者Josip Domazet

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 10:19:58