Flutter中Mockito无法验证带jsonEncode的client.post请求问题
Mockito无法验证使用jsonEncode编码body的POST请求调用
我在为一个简单的POST请求编写测试时遇到问题:当请求body用jsonEncode编码后,Mockito的verify方法无法验证client.post是否被实际调用。
未使用编码时测试正常运行
测试代码
// 其他导入 import 'auth_data_source_test.mocks.dart'; @GenerateMocks([http.Client]) void main() { late AuthRemoteDataSourceImpl datasource; late MockClient mockHttClient; setUp(() { mockHttClient = MockClient(); datasource = AuthRemoteDataSourceImpl(client: mockHttClient); }); group('login', () { const tUsername = 'user@example.com'; const tPassword = 'myPassword'; const tEndpoint = 'http://app.griot.me/api/user/auth/'; const tBody = { "username": tUsername, "password": tPassword, }; const tHeaders = { 'Content-Type': 'application/json', }; final tTokenModel = TokenModel.fromJson(json.decode(fixture('auth_success_response.json'))); test( 'Should perform a POST request with user/auth/ endpoint and application/json header', () async { // arrange when(mockHttClient.post(Uri.parse(tEndpoint), headers: tHeaders, body: tBody)) .thenAnswer((_) async => http.Response(fixture('auth_success_response.json'), 200)); datasource.login(tUsername, tPassword); // assert verify(mockHttClient.post(Uri.parse(tEndpoint), headers: tHeaders, body: {'username': tUsername, 'password': tPassword})); }, ); }
实现代码
// 其他导入 abstract class AuthRemoteDataSource { Future<TokenModel> login(String username, String password); Future<void> storeToken(TokenModel tokenToStore); } class AuthRemoteDataSourceImpl implements AuthRemoteDataSource { final http.Client client; AuthRemoteDataSourceImpl({required this.client}); @override Future<TokenModel> login(String username, String password) async { final Map<String, dynamic> body = { 'username': username, 'password': password, }; final response = await client.post(Uri.parse('http://app.griot.me/api/user/auth/'), headers: { 'Content-Type': 'application/json', }, body: body); await handleError(response); return TokenModel.fromJson(json.decode(response.body)); } @override Future<void> storeToken(TokenModel tokenToStore) async { final SharedPreferences prefs = await SharedPreferences.getInstance(); await prefs.setString('token', tokenToStore.tokenString); } Future<void> handleError(http.Response response) async { if (response.statusCode == 401 || response.statusCode == 404) { throw InvalidTokenException(); } else if (!(response.statusCode >= 200 && response.statusCode <= 204)) { throw ServerException(); } } }
使用jsonEncode后测试失败
修改后的实现代码(仅login方法部分)
@override Future<TokenModel> login(String username, String password) async { final Map<String, dynamic> body = { 'username': username, 'password': password, }; final response = await client.post(Uri.parse('http://app.griot.me/api/user/auth/'), headers: { 'Content-Type': 'application/json', }, body: jsonEncode(body)); // 改为jsonEncode编码 await handleError(response); return TokenModel.fromJson(json.decode(response.body)); }
修改后的测试代码(仅arrange部分)
// arrange when(mockHttClient.post(Uri.parse(tEndpoint), headers: tHeaders, body: jsonEncode(tBody))) .thenAnswer((_) async => http.Response(fixture('auth_success_response.json'), 200));
错误信息
No matching calls. All calls: MockClient.post(http://app.griot.me/api/user/auth/, {headers: {Content-Type: application/json}, body: {"username":"user@example.com","password":"myPassword"}, encoding: null})
已尝试重新运行dart run build_runner build生成Mock文件,但问题仍未解决。
解决方法
问题出在Mockito的参数匹配逻辑上:当使用jsonEncode后,虽然字符串内容看起来一致,但可能存在空格、引号格式或者编码细节差异,导致严格匹配失败。可以用以下方式解决:
1. 预计算编码字符串并复用
提前生成编码后的字符串,避免多次调用jsonEncode可能产生的细微差异:
// 在group块外或setUp中预计算 final tEncodedBody = jsonEncode(tBody); // 测试中使用预计算的字符串 when(mockHttClient.post(Uri.parse(tEndpoint), headers: tHeaders, body: tEncodedBody)) .thenAnswer((_) async => http.Response(fixture('auth_success_response.json'), 200)); verify(mockHttClient.post(Uri.parse(tEndpoint), headers: tHeaders, body: tEncodedBody));
2. 使用argThat进行宽松匹配
通过argThat结合equals方法,验证内容一致性而非严格字符串匹配:
// arrange when(mockHttClient.post(Uri.parse(tEndpoint), headers: tHeaders, body: argThat(equals(jsonEncode(tBody))))) .thenAnswer((_) async => http.Response(fixture('auth_success_response.json'), 200)); // assert verify(mockHttClient.post(Uri.parse(tEndpoint), headers: tHeaders, body: argThat(equals(jsonEncode(tBody)))));
3. 使用any跳过body内容验证(仅验证调用行为)
如果不需要验证body具体内容,可直接用any匹配参数:
// arrange when(mockHttClient.post(Uri.parse(tEndpoint), headers: tHeaders, body: any)) .thenAnswer((_) async => http.Response(fixture('auth_success_response.json'), 200)); // assert verify(mockHttClient.post(Uri.parse(tEndpoint), headers: tHeaders, body: any));
内容的提问来源于stack exchange,提问作者Pedro Brasil
相关产品推荐
相关产品推荐

