首页 > Java > java教程 > 正文

Flutter Dio POST请求的Mockito单元测试指南

花韻仙語
发布: 2025-11-02 14:51:41
原创
955人浏览过

Flutter Dio POST请求的Mockito单元测试指南

本教程将详细指导如何在flutter应用中,使用mockito框架对基于dio库实现的post网络请求进行单元测试,特别是针对登录认证场景。文章将演示如何模拟dio的`post`方法,返回预期的响应数据,从而验证业务逻辑的正确性,确保代码质量和可维护性。

在Flutter开发中,网络请求是常见的业务场景,而Dio是一个功能强大且广泛使用的HTTP客户端。为了确保应用的网络交互逻辑健壮可靠,进行单元测试至关重要。本教程将以一个登录认证的POST请求为例,详细讲解如何结合Mockito库来模拟Dio的行为,从而在不依赖真实网络的情况下测试我们的业务逻辑。

1. 理解待测试的认证服务

首先,我们来看一个典型的登录认证服务。该服务使用Dio发起POST请求,尝试通过用户名和密码获取访问令牌。

import 'package:dio/dio.dart';
import 'package:fluttertoast/fluttertoast.dart'; // 实际测试中通常会mock或忽略UI组件

// 假设的API服务类,提供基础URL
class Apiservices {
  final String baseurl = "https://api.example.com/auth/token"; // 替换为你的实际登录API
}

class AuthenticationService {
  final Dio _dio;
  String? accessToken;
  String? refreshToken;
  final Apiservices _apiServices = Apiservices();

  AuthenticationService(this._dio); // 注入Dio实例

  /// 发起登录认证请求
  /// 成功则返回accessToken,失败则返回DioError的字符串表示
  Future<String?> authentication(String mobileNumber, String password) async {
    dynamic data = {
      "username": mobileNumber,
      "password": password,
      "grant_type": "password",
      "client_id": "client_id"
    };

    try {
      final response = await _dio.post(
        _apiServices.baseurl,
        data: data,
        options: Options(headers: {
          "Content-Type": "application/x-www-form-urlencoded"
        }),
      );

      if (response.statusCode == 200) {
        accessToken = response.data["access_token"];
        refreshToken = response.data["refresh_token"];
        // Fluttertoast.showToast(msg: "Successfully Logged in"); // UI交互,单元测试中通常不直接测试
        return accessToken;
      }
    } on DioError catch (e) {
      // Fluttertoast.showToast(msg: "Something went wrong"); // UI交互
      return e.toString(); // 返回错误信息字符串
    }
    return null; // 如果try块中没有返回,且没有发生DioError,则返回null
  }
}
登录后复制

在这个AuthenticationService中,authentication方法负责构建请求体、发送POST请求,并根据响应状态码处理结果。在单元测试中,我们希望模拟_dio.post的行为,以验证authentication方法在不同响应下的逻辑。

2. 设置单元测试环境

为了进行单元测试,我们需要以下依赖:

  • flutter_test: Flutter的测试框架。
  • mockito: 用于创建模拟对象的库。
  • dio: 被测试的服务所依赖的网络库。
  • build_runner 和 mockito_annotations: 用于自动生成Mockito模拟类。

在pubspec.yaml中添加或更新依赖:

dev_dependencies:
  flutter_test:
    sdk: flutter
  mockito: ^5.4.4 # 请使用最新版本
  build_runner: ^2.4.8 # 请使用最新版本
  mockito_annotations: ^2.3.0 # 请使用最新版本
登录后复制

然后运行 flutter pub get。

接下来,我们需要为Dio类生成一个模拟类。在你的测试文件(例如 test/authentication_service_test.dart)的顶部,添加以下内容:

青柚面试
青柚面试

简单好用的日语面试辅助工具

青柚面试 57
查看详情 青柚面试
// test/authentication_service_test.dart
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';
import 'package:dio/dio.dart';
import 'package:mockito/annotations.dart';

// 导入你的服务文件
import 'package:your_app_name/authentication_service.dart'; // 替换为你的实际路径

// 使用 @GenerateMocks 注解为 Dio 类生成 Mock 类
@GenerateMocks([Dio])
import 'authentication_service_test.mocks.dart'; // 这将在运行 build_runner 后生成
登录后复制

在添加 @GenerateMocks 注解后,运行以下命令来生成模拟文件: flutter pub run build_runner build 这会生成一个 authentication_service_test.mocks.dart 文件,其中包含 MockDio 类。

3. 使用Mockito模拟Dio的POST请求

现在我们可以编写单元测试了。核心思想是使用 when().thenAnswer() 或 when().thenThrow() 来控制 MockDio 对象的行为。

3.1 模拟成功响应

当登录请求成功时,我们期望authentication方法返回一个访问令牌。

// 模拟成功的登录响应数据
final Map<String, dynamic> loginSuccessResponse = {
  "access_token": "mock_access_token_123",
  "refresh_token": "mock_refresh_token_456",
  "expires_in": 3600,
  "token_type": "Bearer"
};

void main() {
  late MockDio mockDio;
  late AuthenticationService authService;

  // 在每个测试运行前初始化模拟对象和待测试服务
  setUp(() {
    mockDio = MockDio();
    authService = AuthenticationService(mockDio);
  });

  group('AuthenticationService', () {
    test('认证成功时应返回访问令牌并设置accessToken', () async {
      // Arrange (安排): 设置模拟Dio的行为
      when(mockDio.post(
        Apiservices().baseurl, // 匹配请求的URL
        data: anyNamed('data'), // 匹配任何名为'data'的参数
        options: anyNamed('options'), // 匹配任何名为'options'的参数
      )).thenAnswer((_) async => Response(
            requestOptions: RequestOptions(path: Apiservices().baseurl), // 必须提供 RequestOptions
            data: loginSuccessResponse,
            statusCode: 200,
          ));

      // Act (执行): 调用待测试的方法
      final result = await authService.authentication("test_mobile", "test_password");

      // Assert (断言): 验证结果是否符合预期
      expect(result, isA<String>()); // 期望返回一个字符串
      expect(result, loginSuccessResponse["access_token"]); // 期望返回正确的访问令牌
      expect(authService.accessToken, loginSuccessResponse["access_token"]); // 验证服务内部的accessToken是否被设置

      // 验证 Dio 的 post 方法是否被正确调用了一次
      verify(mockDio.post(
        Apiservices().baseurl,
        data: anyNamed('data'),
        options: anyNamed('options'),
      )).called(1);
    });
  });
}
登录后复制

代码解释:

  • setUp(): 在每个测试用例运行前,初始化 MockDio 和 AuthenticationService 实例,确保测试之间的隔离性。
  • when(mockDio.post(...)).thenAnswer(...): 这是Mockito的核心。我们告诉 mockDio,当它的 post 方法被调用时,应该执行 thenAnswer 中定义的逻辑。
    • Apiservices().baseurl: 确保我们匹配的是服务中实际使用的URL。
    • anyNamed('data'), anyNamed('options'): 匹配 post 方法中 data 和 options 参数的任何值。如果需要更精确的匹配,可以使用 argThat(equals(...)) 或 captureAny。
    • thenAnswer((_) async => Response(...)): 返回一个 Future<Response>。我们手动构造一个 Response 对象,包含预期的状态码(200)和数据。RequestOptions 是 Response 构造函数所必需的。
  • authService.authentication(...): 调用我们正在测试的实际方法。
  • expect(): 断言结果。我们检查返回类型、返回值以及 authService 内部 accessToken 属性是否被正确更新。
  • verify().called(1): 这是一个重要的验证步骤,确保 mockDio.post 方法确实被调用了一次。

3.2 模拟失败响应(DioError)

当登录请求失败时,例如服务器返回400错误或网络连接问题,我们期望authentication方法捕获DioError并返回其字符串表示。

// 模拟失败的登录响应数据
final Map<String, dynamic> loginFailureResponse = {
  "error": "invalid_grant",
  "error_description": "Bad credentials"
};

// ... (main, setUp, group 保持不变)

  group('AuthenticationService', () {
    // ... (成功测试用例)

    test('认证失败时应返回DioError的字符串表示', () async {
      // Arrange: 设置模拟Dio在post请求时抛出 DioError
      final dioError = DioError(
        requestOptions: RequestOptions(path: Apiservices().baseurl),
        response: Response(
          requestOptions: RequestOptions(path: Apiservices().baseurl),
          data: loginFailureResponse, // 可以包含错误响应体
          statusCode: 400,
        ),
        type: DioErrorType.response, // 指定错误类型
        error: "Bad credentials", // 错误信息
      );

      when(mockDio.post(
        Apiservices().baseurl,
        data: anyNamed('data'),
        options: anyNamed('options'),
      )).thenThrow(dioError); // 模拟抛出 DioError

      // Act: 调用待测试的方法
      final result = await authService.authentication("wrong_mobile", "wrong_password");

      // Assert: 验证结果
      expect(result, isA<String>()); // 期望返回一个字符串
      expect(result, contains("DioError")); // 期望字符串中包含 "DioError"
      expect(result, contains("Bad credentials")); // 期望包含具体的错误信息
      expect(authService.accessToken, isNull); // 验证accessToken未被设置

      verify(mockDio.post(
        Apiservices().baseurl,
        data: anyNamed('data'),
        options: anyNamed('options'),
      )).called(1);
    });
  });
登录后复制

代码解释:

  • thenThrow(dioError): 这次我们让 mockDio.post 方法抛出一个 DioError 实例,而不是返回一个 Response。
  • 我们构造了一个 DioError 对象,其中包含 RequestOptions、可选的 Response(用于表示HTTP错误,如400)、type 和 error 属性。
  • 断言部分检查返回的字符串是否包含 DioError 关键字以及具体的错误描述。

4. 完整示例代码

以下是包含所有必要部分的完整单元测试文件示例:

// test/authentication_service_test.mocks.dart (此文件由 build_runner 自动生成,请勿手动修改)
// import 'package:mockito/mockito.dart';
// import 'package:dio/dio.dart';
// class MockDio extends Mock implements Dio {}

// test/authentication_service_test.dart
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';
import 'package:dio/dio.dart';
import 'package:mockito/annotations.dart';

// 导入你的服务文件
import 'package:your_app_name/authentication_service.dart'; // 替换为你的实际路径

// 使用 @GenerateMocks 注解为 Dio 类生成 Mock 类
@GenerateMocks([Dio])
import 'authentication_service_test.mocks.dart'; // 运行 `flutter pub run build_runner build` 后生成

// 模拟成功的登录响应数据
final Map<String, dynamic> loginSuccessResponse = {
  "access_token": "mock_access_token_123",
  "refresh_token": "mock_refresh_token_456",
  "expires_in": 3600,
  "token_type": "Bearer"
};

// 模拟失败的登录响应数据
final Map<String, dynamic> loginFailureResponse = {
  "error": "invalid_grant",
  "error_description": "Bad credentials"
};

void main() {
  late MockDio mockDio;
  late AuthenticationService authService;

  // 在每个测试运行前初始化模拟对象和待测试服务
  setUp(() {
    mockDio = MockDio();
    authService = AuthenticationService(mockDio);
  });

  group('AuthenticationService', () {
    test('认证成功时应返回访问令牌并设置accessToken', () async {
      // Arrange (安排):
登录后复制

以上就是Flutter Dio POST请求的Mockito单元测试指南的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号