Unit test - How to mock parameters of third-party-library class Dio in flutter

浪尽此生 提交于 2021-01-28 06:30:23

问题


I am trying to test a simple repository class which make a network call using the Dio package that is a dependency injection. Requirement of the Http.post is to send a Map object to a URL with the headers of 'Content-Type': 'application/json. You can see this below:

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'}));
  }
}

I am trying to make sure this method is covered by unit test, however I am having difficult verify the named parameters with the Dio package. I can verify that the dioClient.post is actually called however, I am having trouble with mocking the name parameters of 'data' and 'options'.

I want to test that it gets called with Map<String, String> for the body, and also I would like to test the headers that are sent, I want to check that is it called with the {'Content-Type': 'application/json'}. This will be useful as well when I need to unit test Authenticated dio get/post/put requests.

This is the test that I have so far, which as mentioned before I can verify that the mocked function is called, but not verifying the name params.


  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')));
    });
  });

Thanks for your helps, I am just getting started with unit testing so any help or pointers will be great,

Thanks, Sam

@LoVe

Update

I have implemented your mock class which worked great, and I think it was the thing that I was missing for sure. I have updated my test but cannot understand where I am going wrong now?

test.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)));
    });

and the method file looking like this:

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

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

回答1:


Use http_mock_adapter, new package for mocking Dio requests.

You can simply replace your injected Dio's httpClientAdapter with DioAdapter() of http_mock_adapter:

example from examples of http_mock_adapter

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

void main() async {
  // How to mock with DioAdapter
  group('DioAdapter usage', () {
    // Creating dio instance for mocking.
    // For instance: you can use your own instance from injection and replace
    // dio.httpClientAdapter with mocker DioAdapter
    final dio = Dio();
    final dioAdapter = DioAdapter();

    dio.httpClientAdapter = dioAdapter;

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

    test('Expects Dioadapter to mock the data', () async {
      dioAdapter
          .onGet(path)
          .reply(200,
              {'message': 'Successfully mocked GET!'}) // only use double quotes
          .onPost(path)
          .reply(200, {'message': 'Successfully mocked POST!'});

      // Making dio.get request on the path an expecting mocked response
      final getResponse = await dio.get(path);
      expect(jsonEncode({'message': 'Successfully mocked GET!'}),
          getResponse.data);

      // Making dio.post request on the path an expecting mocked response
      final postResposne = await dio.post(path);
      expect(jsonEncode({'message': 'Successfully mocked POST!'}),
          postResposne.data);
    });
  });

}

You can also use, DioInterceptor of http_mock_adapter, which can be added to dio.interceptor list.

view second example in examples file of http_mock_adapter




回答2:


to test that this method

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

is called with proper arguments you should create an MockOption class and pass an instance of it to the function call in your test

also you should create an object of type Map<String,dynamic> (or whatever types the json uses) and pass that object also to the same method call to the data parameter

then you use Mockito's methods to verify that your method was called with the test arguments you have passed

and also to test the headers you do as I said here regarding the map:

also you should create an object of type Map<String,dynamic> (or whatever types the json uses) and pass that object also to the same method call

Please have a look at my answer here

Update:

so for example to mock the options parameter you can do:

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);
}

then, in your test, you do:

final MockOptions mockOptions = MockOptions({
              //mocked map data here
            });


来源:https://stackoverflow.com/questions/61321828/unit-test-how-to-mock-parameters-of-third-party-library-class-dio-in-flutter

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!