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

Dart使用Mocktail编写单元测试遇类型不匹配错误求助

问题解决:Mock嵌套logIn方法时出现Null类型转换错误

错误根源

你当前的测试代码直接使用MockLoginRepositoryImpl实例调用authenticate方法,但Mockito的Mock类会将所有方法默认返回null——包括你想要测试的authenticate本身,而非仅mock内部的logIn方法。这就导致authenticate执行到await logIn()时,实际调用的是mock的logIn方法(返回null),从而抛出类型转换错误。

解决方案:使用Mockito的Spy功能

Spy可以保留类的真实方法实现,仅mock指定的方法,完美适配你需要测试authenticate真实逻辑、同时mock内部logIn的场景。

修改后的完整测试代码

import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';

class MockBuildContext extends Mock implements BuildContext {}
class MockUtil extends Mock implements Util {}
class MockLoginRepositoryImpl extends Mock implements LoginRepositoryImpl {}

void main() {
  TestWidgetsFlutterBinding.ensureInitialized();
  late LoginRepositoryImpl loginRepository;
  late BuildContext context;
  late Util util;
  late MockLoginRepositoryImpl mockLoginRepo;

  setUp(() {
    context = MockBuildContext();
    util = MockUtil();
    // 创建真实的Repository实例
    loginRepository = LoginRepositoryImpl();
    // 创建Mock实例用于Spy包装
    mockLoginRepo = MockLoginRepositoryImpl();
    // Spy包装:保留真实方法,仅mock指定方法
    loginRepository = spy(loginRepository, mockLoginRepo);
  });

  group('authenticate', () {
    test('authenticate method - valid userId and password', () async {
      // Arrange
      when(() => util.getDeviceId())
          .thenAnswer((_) => Future.value('deviceId'));

      // 使用匹配器确保参数匹配:any()适配复杂类型BuildContext,eq匹配确定参数
      when(() => loginRepository.logIn(
            context: any(),
            credentials: eq('password'),
            deviceId: eq('deviceId'),
            userId: eq('userId'),
          )).thenAnswer((_) => Future.value(UserPermissionApiResponse(
            result: true,
            message: 'Login successful',
          )));

      // Act
      final response = await loginRepository.authenticate(
        'userId',
        'password',
        context,
        util,
      );

      // Assert
      expect(response.result, true);
      expect(response.message, 'Login successful');
      // 验证logIn方法是否按预期被调用
      verify(loginRepository.logIn(
            context: any(),
            credentials: eq('password'),
            deviceId: eq('deviceId'),
            userId: eq('userId'),
          )).called(1);
    });
  });
}

关键修改点说明

  1. 替换Mock为Spy:创建真实的LoginRepositoryImpl实例,再用Spy包装,确保authenticate执行真实逻辑,仅logIn被mock。
  2. 参数匹配优化:对BuildContext这类复杂类型使用any()匹配器,避免因对象相等性判断导致mock不触发;对确定参数(如密码、设备ID)使用eq()精确匹配。
  3. 增加方法调用验证:通过verify()确认logIn方法按预期被调用一次,提升测试的可靠性。

内容的提问来源于stack exchange,提问作者divyanshu verma

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 21:24:54