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

如何使用Mock Cubit实现Flutter Widget测试并解决相关报错

报错原因

  1. 第一个Bad state报错是因为你用mocktail的when直接桩MockCubit的state属性无效:MockCubit的state是bloc_test封装的特殊属性,不能直接用普通的mock方式打桩。
  2. 第二个类型错误是因为删除桩代码后,mock cubit的初始状态默认为null,和你定义的非空AState类型不匹配。

修正步骤

  1. 移除全局的MockDummyCubit实例,改为每个测试用例前单独创建,避免测试用例之间状态污染
  2. 用bloc_test提供的whenListen方法控制MockCubit的状态,这个方法可以同时指定初始状态和后续要emit的状态流

修正后的完整测试代码

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:bloc_test/bloc_test.dart';
import 'package:mocktail/mocktail.dart' as mocktail;

import 'package:counter/dummy_cubit.dart';
import 'package:counter/main.dart';

class MockDummyCubit extends MockCubit<AState> implements DummyCubit {}

class AStateFake extends Fake implements AState {}

void main() {
  setUpAll(() {
    mocktail.registerFallbackValue(AStateFake());
  });

  group('Counter viewer', () {
    late MockDummyCubit dummyCubit;
    // 每个测试前新建mock实例,避免状态污染
    setUp(() {
      dummyCubit = MockDummyCubit();
    });

    Widget getTestWidget(int counter) => MultiBlocProvider(
      providers: [
        BlocProvider<DummyCubit>(
          create: (BuildContext ctx) => dummyCubit,
        ),
      ],
      child: MaterialApp(
        home: CounterViewer(counter: counter),
      ),
    );

    testWidgets('初始状态为InitState时显示加载动画', (WidgetTester tester) async {
      // 指定初始状态为InitState,无后续状态更新
      whenListen(
        dummyCubit,
        Stream.empty(),
        initialState: const InitState(),
      );
      await tester.pumpWidget(getTestWidget(1));
      expect(find.byType(CircularProgressIndicator), findsOneWidget);
    });

    testWidgets('状态为StateLoaded时显示计数文本', (WidgetTester tester) async {
      // 指定初始状态为StateLoaded
      whenListen(
        dummyCubit,
        Stream.empty(),
        initialState: StateLoaded('test'),
      );
      await tester.pumpWidget(getTestWidget(2));
      expect(find.text('2'), findsOneWidget);
      expect(find.byType(CircularProgressIndicator), findsNothing);
    });

    testWidgets('状态切换时UI同步更新', (WidgetTester tester) async {
      // 指定状态流依次输出Loading、Loaded
      whenListen(
        dummyCubit,
        Stream.fromIterable([const StateLoading(), StateLoaded('test')]),
        initialState: const InitState(),
      );
      await tester.pumpWidget(getTestWidget(3));
      // 初始状态显示加载
      expect(find.byType(CircularProgressIndicator), findsOneWidget);
      // 等待状态流发射完成、UI更新
      await tester.pumpAndSettle();
      // 最终显示Loaded状态UI
      expect(find.text('3'), findsOneWidget);
    });
  });
}

控制DummyCubit状态的方法

所有状态控制都通过whenListen方法实现:

  • initialState参数指定MockCubit的初始状态,对应测试启动时的默认状态
  • 第二个参数传入Stream<AState>类型的状态流,流中发射的所有状态都会被MockCubit按顺序emit,触发对应的UI更新

内容的提问来源于stack exchange,提问作者Federico Conte

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 09:24:04