如何为 dio 超时创建测试

How create test for dio timeout

我正在尝试使用 Dio 创建超时测试,我希望得到类型为 CONNECT_TIMEOUT 的 DioError,然后抛出自定义异常

我的测试是用 Mockito 模拟 Dio 并尝试抛出 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>()));
      },
    );

我的数据源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();
      }
    }
  }
}

测试结果为:

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

如何解决这个问题并使用 Dio 创建超时测试?

你的方法有几个问题。

首先,您正在测试 async 方法,但并未 await 测试它。这将导致原始 Future 对象返回到 expect 函数,该函数将认为它是一个成功的调用,即使 future 最终抛出错误。您将需要 await 您的调用,尽管这样做作为传递给 expect 的闭包很尴尬。我建议将异步调用包装在 try/catch 中。

其次,您正在为 Mockito 的 thenThrow 方法提供闭包。这个方法接受你给它的任何东西,并将它用作实际抛出的值,所以它不会调用你传递给它的闭包——它只会按原样抛出它。

解决这两个问题,你会得到这样的结果:

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