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

Flutter测试报错'type 'Null' is not a subtype of type 'Future'及请求体疑问

问题解决与测试方案

一、解决'type 'Null' is not a subtype of type 'Future''错误

这个错误核心是MockHTTPClient的post方法未被正确匹配,导致返回null,具体修复点如下:

  1. 补充调用参数
    原测试调用apiManager.userLoginApi()时未传入email和password,会触发空指针,同时mock的post方法无法匹配带请求体的调用。修复时直接传入测试用假值:
final user = await apiManager.userLoginApi(email: "test@example.com", password: "test123");
  1. 匹配完整的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)));
  1. 修正断言逻辑
    原断言中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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 06:12:24