地理搜索用例单元测试参数匹配失败问题及原因咨询
地理搜索用例单元测试问题解析
我正在为地理搜索用例的实现类编写单元测试,遇到了参数匹配相关的问题,相关代码及错误如下:
实现类代码: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。
需要解答:
- 为何修改实现类的参数传递方式(去掉拼接)能让测试成功?
- 修改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
相关产品推荐
相关产品推荐

