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

Flutter测试Mock AuthBloc状态时遇类型不匹配问题求助

问题:Mock Bloc状态时类型不匹配错误

我在测试Widget对Bloc状态变化的响应时,编写了以下测试代码:

class MockAuthBloc extends Mock implements AuthBloc {}

void main() {
  initHydratedStorage();
  FlavorConfig(
      flavor: Flavor.MOCK,
      baseUrl: 'https://mock.api'
  );
  late MockAuthBloc authBloc;
  late User user;

  setUp(() {
    authBloc = MockAuthBloc();
    user = User(
        email: 'mock@mock.pl',
        name: 'mock',
        hasCompletedProfile: true,
        language: 'pl',
        id: 1
    );
  });

  testWidgets('navigates to MainNavigator when authenticated', (tester) async {
    when(() => authBloc.state).thenReturn(AuthState.authenticated(user));

    await tester.pumpWidget(
      MaterialApp(
        home: BlocProvider<AuthBloc>.value(
          value: authBloc,
          child: const LoginPage(),
        ),
      ),
    );

    await tester.pumpAndSettle();
    expect(find.byType(MainNavigator), findsOneWidget);
  });
}

执行when(() => authBloc.state).thenReturn(AuthState.authenticated(user));时出现错误:

The argument type 'AuthState' can't be assigned to the parameter type 'AuthState Function()'

我的AuthState类定义如下:

part of 'auth_bloc.dart';

enum AuthStatus { unknown, authenticated, guest, failure, semiAuthenticated}


class AuthState {
  final AuthStatus status;
  final bool isFirstEntry;
  final bool isLoadingLong;
  final String? errorMessage;
  final String? email;
  final User? user;

  const AuthState({
    this.status = AuthStatus.unknown,
    this.isFirstEntry = true,
    this.isLoadingLong = false,
    this.errorMessage,
    this.email,
    this.user
  });

  const AuthState.unknown() : this();

  const AuthState.authenticated(User user)
      : this(
    status: AuthStatus.authenticated,
    user: user,
    isFirstEntry: false,
    isLoadingLong: false
  );
}

尝试其他写法也报错:

  • 写法1:

    when(() => authBloc.state).thenReturn(() => AuthState(
          status: AuthStatus.authenticated,
          isFirstEntry: false,
          isLoadingLong: false,
          user: user
        ));
    

    报错:Bad state: No method stub was called from within when(). Was a real method called, or perhaps an extension method?

  • 写法2:

    when(authBloc.state).thenReturn(AuthState(
          status: AuthStatus.authenticated,
          isFirstEntry: false,
          isLoadingLong: false,
          user: user
        ));
    

    报错:type 'Null' is not a subtype of type 'AuthState'


解决方法

问题出在Bloc的state是只读getter属性,Mockito对getter的stub方式和普通方法不同:

  1. 替换thenReturn为thenAnswer,传入一个返回目标状态的回调函数:
when(() => authBloc.state).thenAnswer((_) => AuthState.authenticated(user));

错误原因解释

  • 原写法用thenReturn直接传入AuthState值,但Mockito在stub getter时,期望接收一个返回该值的函数(AuthState Function()类型),thenAnswer正好用来提供这种回调逻辑。
  • 写法1错误:thenReturn需要传入具体值,而非函数,导致类型不匹配。
  • 写法2错误:直接调用authBloc.state会触发mock的默认getter逻辑(返回null),when无法捕获未执行的表达式,从而抛出类型转换错误。

修正后的完整测试代码:

class MockAuthBloc extends Mock implements AuthBloc {}

void main() {
  initHydratedStorage();
  FlavorConfig(
      flavor: Flavor.MOCK,
      baseUrl: 'https://mock.api'
  );
  late MockAuthBloc authBloc;
  late User user;

  setUp(() {
    authBloc = MockAuthBloc();
    user = User(
        email: 'mock@mock.pl',
        name: 'mock',
        hasCompletedProfile: true,
        language: 'pl',
        id: 1
    );
  });

  testWidgets('navigates to MainNavigator when authenticated', (tester) async {
    // 修正后的stub写法
    when(() => authBloc.state).thenAnswer((_) => AuthState.authenticated(user));

    await tester.pumpWidget(
      MaterialApp(
        home: BlocProvider<AuthBloc>.value(
          value: authBloc,
          child: const LoginPage(),
        ),
      ),
    );

    await tester.pumpAndSettle();
    expect(find.byType(MainNavigator), findsOneWidget);
  });
}

内容的提问来源于stack exchange,提问作者Karol Wiśniewski

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 13:03:25