Flutter测试报错'type 'Null' is not a subtype of type 'Future'及请求体疑问
问题解决与测试方案
一、解决'type 'Null' is not a subtype of type 'Future''错误
这个错误核心是MockHTTPClient的post方法未被正确匹配,导致返回null,具体修复点如下:
- 补充调用参数
原测试调用apiManager.userLoginApi()时未传入email和password,会触发空指针,同时mock的post方法无法匹配带请求体的调用。修复时直接传入测试用假值:
final user = await apiManager.userLoginApi(email: "test@example.com", password: "test123");
- 匹配完整的post请求参数
真实登录接口的post请求必然携带请求体,原测试仅匹配Uri,导致mock失效。修复时在mock中指定请求体参数:
final requestBody = jsonEncode({ "email": "test@example.com", "password": "test123" }); when(() => mockHTTPClient.post( Uri.parse(url), body: requestBody, )).thenAnswer((_) => Future.value(Response(mockResponse, 200)));
- 修正断言逻辑
原断言中isuserExist未定义,且isA<Data()>写法错误,改为用实际返回的user变量:
expect(user, isA<Data>());
二、登录API测试的请求体处理方案
测试完全不需要真实凭证,按以下方式处理:
- 传入测试假值:直接给
email和password传合法的测试字符串(如"test@example.com"、"test123"),无需真实有效账号。 - Mock时匹配请求体:确保mock的post方法
body参数与userLoginApi实际发送的请求体完全一致(注意用jsonEncode处理Map),保证mock触发。 - 验证请求体(可选):如果要确认
userLoginApi是否正确构造请求体,可在测试末尾添加验证:verify(() => mockHTTPClient.post( Uri.parse(url), body: jsonEncode({"email": "test@example.com", "password": "test123"}), )).called(1);
修复后的完整测试代码示例
import 'dart:convert'; import 'package:test/test.dart'; import 'package:http/http.dart' as http; import 'package:mockito/mockito.dart'; class MockHTTPClient extends Mock implements http.Client {} class ApiManager { final http.Client client; ApiManager(this.client); Future<Data> userLoginApi({String? email, String? password}) async { final response = await client.post( Uri.parse('url'), body: jsonEncode({ "email": email, "password": password, }), ); if (response.statusCode == 200) { return Data.fromJson(jsonDecode(response.body)); } throw Exception('Some Error Occurred'); } } class Data { final String status; Data({required this.status}); factory Data.fromJson(Map<String, dynamic> json) { return Data(status: json['status']); } } void main() { late ApiManager apiManager; late MockHTTPClient mockHTTPClient; setUp(() { mockHTTPClient = MockHTTPClient(); apiManager = ApiManager(mockHTTPClient); }); test( 'given ApiManager when userLoginApi is called with valid credentials and status code is 200 then a Data model should be returned', () async { // Arrange const mockResponse = '{"status": "200"}'; const url = "url"; const testEmail = "test@example.com"; const testPassword = "test123"; final requestBody = jsonEncode({ "email": testEmail, "password": testPassword, }); when(() => mockHTTPClient.post( Uri.parse(url), body: requestBody, )).thenAnswer((_) => Future.value(http.Response(mockResponse, 200))); // Act final user = await apiManager.userLoginApi(email: testEmail, password: testPassword); // Assert expect(user, isA<Data>()); expect(user.status, "200"); verify(() => mockHTTPClient.post( Uri.parse(url), body: requestBody, )).called(1); }, ); }
注:原userLoginApi逻辑存在问题——响应码200时未解析接口返回的response.body,而是自行构造Map,修复时改为解析真实响应体。
内容的提问来源于stack exchange,提问作者Riot
相关产品推荐
相关产品推荐

