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

地理搜索用例单元测试参数匹配失败问题及原因咨询

地理搜索用例单元测试问题解析

我正在为地理搜索用例的实现类编写单元测试,遇到了参数匹配相关的问题,相关代码及错误如下:

实现类代码:search_location_usecase_impl.dart

class SearchLocationUsecaseImpl implements SearchLocationUsecase {
  SearchLocationUsecaseImpl({
    required GeographySearchRepository geographySearchRepository,
    required String geographySearchApiKey,
    required FirebaseCrashlytics firebaseCrashlyticsInstance,
  })  : _geographySearchRepository = geographySearchRepository,
        _geographySearchApiKey = geographySearchApiKey,
        _firebaseCrashlyticsInstance = firebaseCrashlyticsInstance;

  final GeographySearchRepository _geographySearchRepository;
  final String _geographySearchApiKey;
  final FirebaseCrashlytics _firebaseCrashlyticsInstance;

  @override
  Future<LatLng> execute({required String requestPath}) async {
    try {
      final resultLocation = await _geographySearchRepository.searchLocation(
        requestPath: requestPath + _geographySearchApiKey,
      );
      if (resultLocation == null) {
        throw SearchLocationException();
      }
      return resultLocation.toLatLng();
    } on Exception catch (_) {
      await _firebaseCrashlyticsInstance.log(SearchLocationException().code);
      throw SearchLocationException();
    }
  }
}

单元测试代码:search_location_usecase_impl_test.dart

void main() {
  late MockGeographySearchRepository mockGeographySearchRepository;
  late SearchLocationUsecase searchLocationUsecase;
  final fakeFirebaseCrashlytics = MockFirebaseCrashlytics();

  setUp(() {
    mockGeographySearchRepository = MockGeographySearchRepository();
    searchLocationUsecase = SearchLocationUsecaseImpl(
      geographySearchRepository: mockGeographySearchRepository,
      geographySearchApiKey: TestConst.geographySearchApiKey,
      firebaseCrashlyticsInstance: fakeFirebaseCrashlytics,
    );
  });

  group('[UnitTest: SearchLocationUsecase]', () {
    test('', () async {
      final resultLocation = ModelConst.resultLocation.toLatLng();
      // Arrange
      when(
        mockGeographySearchRepository.searchLocation(
          requestPath: TestConst.requestPath,
        ),
      ).thenAnswer((_) async => ModelConst.resultLocation);

      // Act
      final result = await searchLocationUsecase.execute(
        requestPath: TestConst.requestPath,
      );
      debugPrint(result.toString());

      // Assert
      expect(result, resultLocation);
    });
  });
}

运行测试时的错误

Instance of 'SearchLocationException'
  package:____/usecase/search_location_usecase_impl.dart 31:7  SearchLocationUsecaseImpl.execute

问题

删除实现类中requestPath + _geographySearchApiKey的拼接逻辑,改为直接传入requestPath后测试可正常运行;但尝试将Mock的参数改为TestConst.requestPath + geographySearchApiKey时仍会抛出相同的SearchLocationException。

需要解答:

  1. 为何修改实现类的参数传递方式(去掉拼接)能让测试成功?
  2. 修改Mock参数后仍报错的原因是什么?

问题1:去掉拼接后测试成功的原因

Mockito对方法调用的匹配是严格的相等匹配。当你去掉拼接逻辑,实现类调用searchLocation时传入的参数就是TestConst.requestPath,和你在Mock中预设的参数完全一致,此时Mock会触发预设的返回值ModelConst.resultLocation,实现类不会进入异常分支,测试自然通过。

问题2:修改Mock参数后仍报错的原因

核心原因是你修改后的Mock参数和实现类实际传入的参数不匹配,导致Mock的预设逻辑没有被触发:

  • 测试代码中你写的geographySearchApiKey可能是未定义的变量,或者你拼接的不是实现类初始化时传入的TestConst.geographySearchApiKey,导致拼接后的字符串和实现类中requestPath + _geographySearchApiKey的结果不一样。
  • Mockito的字符串匹配是精确的,哪怕有一个字符、空格或者大小写差异,都会导致匹配失败。
  • 当Mock的方法调用条件未命中时,Mockito默认返回null,实现类会触发if (resultLocation == null)的判断,抛出SearchLocationException,也就是你看到的错误。

快速修复方案

确保Mock的参数和实现类实际调用的参数完全一致:

// 用TestConst中的api key拼接,和实现类逻辑完全对齐
when(
  mockGeographySearchRepository.searchLocation(
    requestPath: TestConst.requestPath + TestConst.geographySearchApiKey,
  ),
).thenAnswer((_) async => ModelConst.resultLocation);

如果还是不确定,可以用参数捕获器验证实际传入的参数:

final argCaptor = ArgumentCaptor<String>();
// 执行测试后验证调用参数
verify(mockGeographySearchRepository.searchLocation(requestPath: argCaptor.capture()));
print(argCaptor.value); // 打印实际传入的参数,和你Mock的参数对比找差异

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 03:53:22