首页 > 解决方案 > 单元测试 - 如何在颤振中模拟第三方库类 Dio 的参数

问题描述

我正在尝试测试一个简单的存储库类,它使用依赖注入的 Dio 包进行网络调用。Http.post 的要求是将 Map 对象发送到带有'Content-Type': 'application/json. 您可以在下面看到:

class AuthenticateUserRemoteDataSourceImpl
    implements AuthenticateUserRepository {
  final Dio httpClient;

  AuthenticateUserRemoteDataSourceImpl({@required this.httpClient});

  @override
  Future<Either<Failure, AuthenticateUser>> getAuthenticateUser(
      String email, String password) async {
    final url = 'API_URL';

    final Map<String, String> jsonPayload = {
      "email": email,
      "password": password
    };

    Response response = await httpClient.post('{$url}api/users/authenticate',
        data: jsonPayload,
        options: Options(headers: {'Content-Type': 'application/json'}));
  }
}

我试图确保单元测试涵盖此方法,但是我很难使用 Dio 包验证命名参数。我可以验证dioClient.post实际调用了但是,我在模拟“数据”和“选项”的名称参数时遇到了麻烦。

我想测试它是否被Map<String, String>正文调用,并且我想测试发送的标头,我想检查它是用{'Content-Type': 'application/json'}. 当我需要对 Authenticated dio get/post/put 请求进行单元测试时,这也会很有用。

这是我到目前为止所做的测试,如前所述,我可以验证是否调用了模拟函数,但不能验证名称参数。


  group('getAuthenticateUser', () {
    final tResponse = Response(statusCode: 200, data: {"title": "success"});

    test(
        'should perform a post request to the the API with the application/json header',
        () async {
      // arrange
      when(dioClient.post(any,
              data: anyNamed('data'), options: anyNamed('options')))
          .thenAnswer((Invocation invocation) async => tResponse);

      // act
      await dataSource.getAuthenticateUser(tEmail, tPassword);

      // assert
      verify(dioClient.post(any,
          data: anyNamed('data'), options: anyNamed('options')));
    });
  });

感谢您的帮助,我刚刚开始进行单元测试,所以任何帮助或指示都会很棒,

谢谢,山姆

@爱 在此处输入图像描述

更新

我已经实现了你的模拟类,效果很好,我认为这是我肯定缺少的东西。我已经更新了我的测试,但不明白我现在哪里出错了?

测试.dart

class MockOptions extends Mock implements Options {
  final Map<String, dynamic> headers;
  //also add any other parameters you want to mock as fields in this class

  MockOptions({this.headers});
}


test(
   'should perform a post request to the the API with the application/json header',
    () async {
        // arrange
        Map<String, String> headers = {'Content-type': 'application/json'};

        when(mockDio.post('path', options: anyNamed('options')))
          .thenAnswer((_) async => Response(data: {}));

        // act
        dataSource.getAuthenticateUser(tEmail, tPassword);

        // assert
        verify(mockDio.post('path', options: MockOptions(headers: headers)));
    });

方法文件如下所示:

  @override
  Future<Either<Failure, AuthenticateUser>> getAuthenticateUser(
      String email, String password) async {

    await this.httpClient.post('path',
        data: {},
        options: Options(headers: {'Content-type': 'application/json'}));
  }

标签: httpflutterdartmockitoflutter-test

解决方案


使用http_mock_adapter,用于模拟请求的新包Dio

您可以简单地将注入Dio的 'shttpClientAdapter替换DioAdapter()http_mock_adapter

http_mock_adapter示例中的示例

import 'package:dio/dio.dart';
import 'package:http_mock_adapter/http_mock_adapter.dart';

void main() async {
  final dio = Dio();
  final dioAdapter = DioAdapter();

  dio.httpClientAdapter = dioAdapter;

  const path = 'https://example.com';

  dioAdapter
      ..onGet(
        path,
        (request) => request.reply(200, {'message': 'Successfully mocked GET!'}),
      )
      ..onGet(
        path,
        (request) => request.reply(200, {'message': 'Successfully mocked POST!'}),
      );

  final onGetResponse = await dio.get(path);
  print(onGetResponse.data); // {message: Successfully mocked GET!}

  final onPostResponse = await dio.post(path);
  print(onPostResponse.data); // {message: Successfully mocked POST!}
}

您也可以使用DioInterceptorhttp_mock_adapter 它可以添加到dio.interceptor列表中。

在http_mock_adapter的示例文件中查看第二个示例


推荐阅读