Search code examples
flutterunit-testingdartdio

How create test for dio timeout


I am trying to create a test for timeout using Dio, I expect get DioError with type CONNECT_TIMEOUT then throw a custom exception

My test I mock Dio with Mockito and try throw DioError

test(
      'Should throw [ConnectionTimeOutException] when reach timeout',
      () async {
        //arange
        when(mockNetworkInfo.isConnected).thenAnswer((_) async => true);
        when(mockDio.post(paths.login, data: tParams.toJson())).thenThrow(
            (_) async => DioError(type: DioErrorType.CONNECT_TIMEOUT));
        //act
        final call = loginDataSource.login;
        //assert
        expect(() => call(params: tParams),
            throwsA(TypeMatcher<ConnectTimeOutException>()));
      },
    );

My data source class:

class LoginDataSourceImpl implements LoginDataSource {
  final Dio dio;
  final NetworkInfo networkInfo;

  LoginDataSourceImpl({@required this.dio, @required this.networkInfo});

  @override
  Future<CredencialModel> login({@required Params params}) async {
    if (!await networkInfo.isConnected) {
      throw NoNetworkException();
    }

    try {
      final response = await dio.post(paths.login, data: params.toJson());
      if (response.statusCode == 200) {
        return CredencialModel.fromJson(response.data);
      } else if (response.statusCode == 400) {
        final error = ResponseError.fromJson(response.data);
        switch (error.error) {
          case 'invalid_request':
            throw InvalidRequestException();
            break;
          case 'invalid_device':
            throw InvalidDeviceException();
            break;
          case 'invalid_user_credentials':
            throw InvalidUserCredentialException();
            break;
          case 'user_disabled':
            throw UserDisableException();
          default:
            throw UnknowException();
        }
      } else if (response.statusCode == 500) {
        throw ServerException();
      } else {
        throw UnknowException();
      }
    } on DioError catch (e) {
      if (e.type == DioErrorType.CONNECT_TIMEOUT) {
        throw ConnectTimeOutException();
      } else if (e.type == DioErrorType.RECEIVE_TIMEOUT) {
      } else {
        throw UnknowException();
      }
    }
  }
}

The result of the test is:

Expected: throws <Instance of 'ConnectTimeOutException'>
  Actual: <Closure: () => Future<CredencialModel>>
   Which: threw <Closure: (dynamic) => DioError>
stack package:mockito/src/mock.dart 385:7    

How can i solve this issue and create a Timeout test with Dio?


Solution

  • There are a couple of problems with your approach.

    First, you are testing an async method but you are not awaiting it. This is going to cause the raw Future object to be returned to the expect function which is going to consider it a successful call, even if the future ends up throwing an error. You will need to await your call, although doing so as a closure passed to expect is awkward. I would suggest wrapping the asynchronous call in a try/catch instead.

    Second, you are providing a closure to Mockito's thenThrow method. This method takes whatever you give to it and uses it as the actual thrown value, so it isn't going to call the closure you passed to it - it will just throw it as-is.

    Fixing these both, you end up with this:

    test(
      'Should throw [ConnectionTimeOutException] when reach timeout',
      () async {
        // arrange
        when(mockNetworkInfo.isConnected)
          .thenAnswer(true);
        when(mockDio.post(paths.login, data: tParams.toJson()))
          .thenThrow(DioError(type: DioErrorType.CONNECT_TIMEOUT));
    
        // act
        final call = loginDataSource.login;
    
        // assert
        try {
          await call(params: tParams);
        } catch(e) {
          expect(e, isInstanceOf<ConnectTimeOutException>());
        }
      },
    );