微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Flutter Bloc 测试未捕获 thenThrow WebException

如何解决Flutter Bloc 测试未捕获 thenThrow WebException

我正在尝试在我的 Flutter 应用程序中测试 BLoC,但我在下面遇到了这个问题。

bool safecopy(byte* out,const byte* from,size_t size) {
    const auto pmask = PAGE_READONLY | PAGE_READWRITE | PAGE_WRITEcopY | PAGE_EXECUTE |
        PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITEcopY;
    auto at = from;
    auto till = at + size;
    ::MEMORY_BASIC_informatION mbi{};
    while (at < till) {
        if (!VirtualQuery(at,&mbi,sizeof mbi)) return false;
        if ((mbi.State == MEM_COMMIT) && !(mbi.Protect & PAGE_GUARD) && (mbi.Protect & pmask)) {
            const byte* m_begin = static_cast<const byte*>(mbi.BaseAddress);
            const byte* m_end = m_begin + mbi.RegionSize;
            at = m_end;
            if (m_end > till) m_end = till;
            if (m_begin < from) m_begin = from;
            memcpy(out + (m_begin - from),m_begin,m_end - m_begin);
        } else return false;
    }
    return true;
}

这是由这个 Failing BLoC 测试引起的

===== asynchronous gap ===========================
dart:async                                                 _AsyncAwaitCompleter.completeError
package:bloc_test/src/bloc_test.dart                       runBlocTest.<fn>
dart:async                                                 runZoned
package:bloc_test/src/bloc_test.dart 157:9                 runBlocTest
package:bloc_test/src/bloc_test.dart 127:11                blocTest.<fn>

Expected: [
            ChangePasswordLoading:ChangePasswordLoading,ChangePasswordFailure:ChangePasswordFailure
          ]
  Actual: [
            ChangePasswordLoading:ChangePasswordLoading,ChangePasswordSuccess:ChangePasswordSuccess
          ]
   Which: at location [1] is ChangePasswordSuccess:<ChangePasswordSuccess> instead of ChangePasswordFailure:<ChangePasswordFailure>

package:test_api                             expect
package:bloc_test/src/bloc_test.dart 176:9   runBlocTest.<fn>
===== asynchronous gap ===========================
dart:async                                   _asyncThenWrapperHelper
package:bloc_test/src/bloc_test.dart         runBlocTest.<fn>
dart:async                                   runZoned
package:bloc_test/src/bloc_test.dart 157:9   runBlocTest
package:bloc_test/src/bloc_test.dart 127:11  blocTest.<fn>

这是我用来测试我的 ChangePasswordBloc 的代码(注意所有其他测试都成功通过)

blocTest<ChangePasswordBloc,ChangePasswordState>(
      'emits [ChangePasswordLoading,ChangePasswordFailure] on Failed ChangePassword',build: () {
        when(authenticationRepository.changePassword(
          'token','oldPassword','newPassword','newPasswordConfirm',)).thenThrow(WebException(403));
        return changePasswordBloc;
      },act: (bloc) => bloc
        ..add(ChangePassword(
          oldPassword: 'oldPassword',newPassword: 'newPassword',newPasswordConfirm: 'newPasswordConfirm',)),expect: [
        ChangePasswordLoading(),ChangePasswordFailure(error: 'Old password is not correct'),],errors: [isA<WebException>()],);

这是我的 ChangePasswordBloc 代码

ChangePasswordBloc

import 'package:bloc_test/bloc_test.dart';
import 'package:Flutter_app/business_logic/blocs/change_password/change_password_bloc.dart';
import 'package:Flutter_app/business_logic/blocs/change_password/change_password_event.dart';
import 'package:Flutter_app/business_logic/blocs/change_password/change_password_state.dart';
import 'package:Flutter_app/data/exceptions/web_exception.dart';
import 'package:Flutter_app/data/repositories/authentication_repository.dart';
import 'package:Flutter_secure_storage/Flutter_secure_storage.dart';
import 'package:Flutter_test/Flutter_test.dart';
import 'package:mockito/mockito.dart';

class MockAuthenticationRepository extends Mock
    implements AuthenticationRepository {}

class MockSecureStorage extends Mock implements FlutterSecureStorage {}

main() {
  ChangePasswordBloc changePasswordBloc;
  MockSecureStorage secureStorage;
  MockAuthenticationRepository authenticationRepository;

  setUp(() {
    secureStorage = MockSecureStorage();
    authenticationRepository = MockAuthenticationRepository();
    changePasswordBloc = ChangePasswordBloc(
      authenticationRepository,secureStorage,);
  });

  tearDown(() {
    changePasswordBloc?.close();
  });

  test(
    'initial state is ChangePasswordInitial',() => expect(changePasswordBloc.state,ChangePasswordInitial()),);

  group('ChangePassword process',() {
    blocTest<ChangePasswordBloc,ChangePasswordSuccess] on successful ChangePassword',)).thenAnswer((_) async => null);
        return changePasswordBloc;
      },ChangePasswordSuccess(),);

    blocTest<ChangePasswordBloc,);
  });
}

如您所知,如果抛出 WebException,我会生成 ChangePasswordFailure() 并显示错误消息。这确实适用于实际的应用程序,所以我确信逻辑有效,但测试似乎没有捕捉到抛出的 WebException。

更改密码事件

import 'dart:async';
import 'package:Flutter_app/data/exceptions/web_exception.dart';
import 'package:Flutter_app/data/repositories/authentication_repository.dart';
import 'package:Flutter_secure_storage/Flutter_secure_storage.dart';
import 'package:bloc/bloc.dart';
import 'change_password_event.dart';
import 'change_password_state.dart';

class ChangePasswordBloc
    extends Bloc<ChangePasswordEvent,ChangePasswordState> {
  final AuthenticationRepository _authenticationRepository;
  final FlutterSecureStorage _secureStorage;

  ChangePasswordBloc(AuthenticationRepository authenticationRepository,FlutterSecureStorage secureStorage)
      : _authenticationRepository = authenticationRepository,_secureStorage = secureStorage,super(ChangePasswordInitial());

  @override
  Stream<ChangePasswordState> mapEventToState(
    ChangePasswordEvent event,) async* {
    if (event is ChangePassword) {
      yield* _mapChangePasswordToState(event);
    }
  }

  Stream<ChangePasswordState> _mapChangePasswordToState(
      ChangePassword event) async* {
    yield ChangePasswordLoading();
    try {
      final accesstoken = await _secureStorage.read(key: 'accesstoken');

      await _authenticationRepository.changePassword(
        accesstoken,event.oldPassword,event.newPassword,event.newPasswordConfirm,);
      yield ChangePasswordSuccess();
    } on WebException catch (e) {
      String errorMessage;
      if (e.statusCode == 422) {
        errorMessage = 'Password must be 8 characters long';
      } else if (e.statusCode == 419) {
        errorMessage = 'New Password is not matching';
      } else if (e.statusCode == 403) {
        errorMessage = 'Old password is not correct';
      }
      yield ChangePasswordFailure(error: errorMessage ?? e.toString());
    } catch (err) {
      yield ChangePasswordFailure(
          error: err.toString() ?? 'An unkNown error occurred');
    }
  }
}

更改密码状态

import 'package:equatable/equatable.dart';
import 'package:Meta/Meta.dart';

abstract class ChangePasswordEvent extends Equatable {
  @override
  List<Object> get props => [];
}

class ChangePassword extends ChangePasswordEvent {
  final String oldPassword;
  final String newPassword;
  final String newPasswordConfirm;

  ChangePassword({
    @required this.oldPassword,@required this.newPassword,@required this.newPasswordConfirm,});

  @override
  List<Object> get props => [oldPassword,newPassword,newPasswordConfirm];
}

关于为什么 .thenThrow(WebException(403)) 在真正的 Flutter App 上实际工作时实际上没有被捕获的任何建议或建议(如果抛出 WebException,则总是抛出 ChangePasswordFailure)?

我有一个相同代码的例子,它确实有效(ClientInfoBloc的代码以与ChangePasswordBloc相同的方式处理WebExceptions,它也适用于真正的Flutter应用程序)

Working Test Example with thrown WebException

我检查了这个相关的 issue 但它没有解决任何问题。

解决方法

我认为您需要添加另一个“when”块来模拟这一行: final accessToken = await _secureStorage.read(key: 'accessToken'); 像这样: when(secureStorage.read(key: 'accessToken')).thenAnswer(() async => 'token'); 或删除当前“when”块的参数“token”。 因为您目前是说,当您使用属性 accessToken = 'token' 调用 'changePassword' 方法时,它会抛出错误,而这不是真的,因为 accessToken = null。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。