如何实现 BlocTest 功能?

How to implement BlocTest function?

我正在尝试从身份验证功能开始为我的 flutter 应用程序实施 blocTesting。以下是为此所需的身份验证和登录相关文件。如果有人能告诉我如何根据我的代码实施 blocTesting,我将不胜感激,因为我在这样做时遇到了问题。以下是 auth bloc 的 bloc、state 和 event 文件。

Authbloc.dart

import 'dart:async';

import 'package:bloc/bloc.dart';
import 'package:core/models/exception/kbm_exception.dart';
import 'package:equatable/equatable.dart';
import 'package:mynovatium/app/model/global_countries_model.dart';
import 'package:mynovatium/features/login/repositories/authentication_repository.dart';
import 'package:mynovatium/features/settings/models/notification_model.dart';
import 'package:mynovatium/features/settings/models/reason_model.dart';
import 'package:mynovatium/features/settings/models/settings_model.dart';
import 'package:mynovatium/features/settings/models/sysconfig_model.dart';
import 'package:mynovatium/features/settings/repositories/settings_repository.dart';

part 'authentication_event.dart';
part 'authentication_state.dart';

class AuthenticationBloc extends Bloc<AuthenticationEvent, AuthenticationState> {
  final AuthenticationRepository authenticationRepository = AuthenticationRepository();
  final SettingsRepository _settingsRepository = SettingsRepository();
  AuthenticationBloc() : super(AuthenticationInitial()) {
    // Register events here
    on<AuthenticationStarted>(_onAuthenticationStarted);
    on<AuthenticationLoggedIn>(_onAuthenticationLoggedIn);
    on<AuthenticationLoggedOut>(_onAuthenticationLoggedOut);
  }

  Future<void> _onAuthenticationStarted(AuthenticationStarted event, Emitter<AuthenticationState> emit) async {
    try {
      final bool hasToken = await authenticationRepository.hasToken();
      if (hasToken) {
        final Settings _settings = await _settingsRepository.getSettings();
        final SysConfig _sysConfig = await _settingsRepository.getSysconfig();
        final CountriesModelList _countries = await _settingsRepository.getCountries();
        final ReasonsModelList _reasons = await _settingsRepository.getReasons();
        final NotificationOptionsList _notificationOptions = await _settingsRepository.getNotificationOptions();
        emit(
          AuthenticationLoadSuccess(
            settings: _settings,
            sysConfig: _sysConfig,
            countries: _countries,
            reasons: _reasons,
            notificationOptions: _notificationOptions,
          ),
        );
      } else {
        emit(AuthenticationUnauthenticated());
      }
    } catch (e) {
      final KBMException _exception = e as KBMException;
      emit(AuthenticationLoadFailure(exception: _exception));
    }
  }

  Future<void> _onAuthenticationLoggedIn(AuthenticationLoggedIn event, Emitter<AuthenticationState> emit) async {
    emit(AuthenticationLoadInProgress());
    await authenticationRepository.persistToken(event.token);
    final Settings _settings = await _settingsRepository.getSettings();
    final SysConfig _sysConfig = await _settingsRepository.getSysconfig();
    final CountriesModelList _countries = await _settingsRepository.getCountries();
    final ReasonsModelList _reasons = await _settingsRepository.getReasons();
    final NotificationOptionsList _notificationOptions = await _settingsRepository.getNotificationOptions();
    emit(
      AuthenticationLoadSuccess(
        settings: _settings,
        sysConfig: _sysConfig,
        countries: _countries,
        reasons: _reasons,
        notificationOptions: _notificationOptions,
      ),
    );
  }

  Future<void> _onAuthenticationLoggedOut(AuthenticationLoggedOut event, Emitter<AuthenticationState> emit) async {
    await authenticationRepository.deleteToken();
    await Future<dynamic>.delayed(const Duration(seconds: 2));
    emit(AuthenticationUnauthenticated());

    add(AuthenticationStarted());
  }
}

Authstate.dart

part of 'authentication_bloc.dart';

abstract class AuthenticationEvent extends Equatable {
  const AuthenticationEvent();

  @override
  List<Object> get props => <Object>[];
}

class AuthenticationStarted extends AuthenticationEvent {}

class AuthenticationLoggedIn extends AuthenticationEvent {
  final String token;

  const AuthenticationLoggedIn({required this.token});

  @override
  List<Object> get props => <Object>[token];
}

class AuthenticationLoggedOut extends AuthenticationEvent {}

AuthEvent.dart

part of 'authentication_bloc.dart';

abstract class AuthenticationState extends Equatable {
  const AuthenticationState();

  @override
  List<Object> get props => <Object>[];
}

class AuthenticationInitial extends AuthenticationState {}

class AuthenticationUnauthenticated extends AuthenticationState {}

class AuthenticationLoadSuccess extends AuthenticationState {
  final SysConfig sysConfig;
  final Settings settings;
  final CountriesModelList countries;
  final ReasonsModelList reasons;
  final NotificationOptionsList notificationOptions;

  const AuthenticationLoadSuccess({required this.sysConfig, required this.settings, required this.countries, required this.reasons, required this.notificationOptions});

  @override
  List<Object> get props => <Object>[sysConfig, settings, countries, reasons, notificationOptions];
}

class AuthenticationLoadInProgress extends AuthenticationState {}

class AuthenticationLoadFailure extends AuthenticationState {
  final KBMException exception;
  const AuthenticationLoadFailure({required this.exception});
  @override
  List<Object> get props => <Object>[exception];
}

我已经尝试编写以下文件,但我不知道如何继续。我的老板说我需要在测试 Bloc_test 函数中使用真实的 apis。

Authbloctest.dart

import 'package:bloc_test/bloc_test.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_test/flutter_test.dart';
// ignore: depend_on_referenced_packages
import 'package:mocktail/mocktail.dart';
import 'package:mynovatium/features/login/bloc/authentication/authentication_bloc.dart';
import 'package:mynovatium/features/login/repositories/authentication_repository.dart';

class MockWeatherRepository extends Mock implements AuthenticationRepository {}

class TestUser {
  final String username;
  final String password;
  final String role;

  TestUser(this.username, this.password, this.role);
}

final List<TestUser> _testUsers = [
  TestUser('me@gmail.com', '****', 'abc_user'),
];

void main() async {
;
  
  group('AuthenticationBloc', () {
    AuthenticationRepository authenticationRepositoryMock;
    AuthenticationBloc authenticationBloc;

    setUp(() {
      authenticationRepositoryMock = MockWeatherRepository();
      authenticationBloc = AuthenticationBloc();
    });

    test('initial state of the bloc is [AuthenticationInitial]', () {
      expect(AuthenticationBloc().state, AuthenticationInitial());
    });



    group('AuthenticationStarted', () {
     blocTest<AuthenticationBloc, AuthenticationState>(
      'emits [AuthenticationInitial, AuthenticationLoadInProgress, AuthenticationLoadSuccess] '
      'state when successfully authenticated',
      setUp: () {
        
      },
      build: () => AuthenticationBloc(),
      // act: (bloc) => bloc.add(),
      expect: () => [AuthenticationInitial(),
       AuthenticationLoadInProgress(),
      //  AuthenticationLoadSuccess(
      //    sysConfig: sysConfig,
      //    settings: settings, 
      //    countries: countries, 
      //    reasons: reasons, 
      //    notificationOptions: notificationOptions)
         ],
    );
    });
  });
}

你必须改变很多想法。 首先,您需要将 repository/ies 添加到您的 bloc 构造函数中以注入模拟。

class AuthenticationBloc extends Bloc<AuthenticationEvent, AuthenticationState> {
  late final AuthenticationRepository authenticationRepository;
  final SettingsRepository _settingsRepository = SettingsRepository();
  AuthenticationBloc({required this.authenticationRepository}) : super(AuthenticationInitial()) {
    // Register events here
    on<AuthenticationStarted>(_onAuthenticationStarted);
    on<AuthenticationLoggedIn>(_onAuthenticationLoggedIn);
    on<AuthenticationLoggedOut>(_onAuthenticationLoggedOut);
  }

然后在setup方法中创建bloc时就可以使用mock

setUp(() {
      authenticationRepositoryMock = MockWeatherRepository();
      authenticationBloc = AuthenticationBloc(authenticationRepository: authenticationRepositoryMock );
    });

然后你必须 return 在你的 blocTest 的构建函数中那个 bloc 并且你还必须在那里设置模拟行为

build: () {
        when(() => authenticationRepositoryMock .hasToken()).thenAnswer((_) async => true);
        return bloc;
      },

然后在 act 函数中向您的 bloc 添加一个事件

act: (dynamic b) => b.add(AuthenticationStarted()),

然后就可以在expect函数中查看结果了。 (我认为这里不会发出初始状态)

expect: () => [
    AuthenticationLoadSuccess(...),

模拟 SettingsRepository 也是一个好主意。