본문 바로가기
IT-dev

[Flutter] 플러터 Bloc 코드 테스트 bloc_test 사용법

by 김현진_ 2024. 6. 2.

 

bloc_test 패키지 https://pub.dev/packages/bloc_test

오늘은 bloc를 활용한 코드를 작성할때, 어떻게 테스트 코드를 작성하는지 알아보겠습니다.

1. Mock 클래스 작성

import 'package:bloc_test/bloc_test.dart';

/// Bloc Mock 클래스, Subject을 바꿔서 작성
class MockSubjectBloc extends MockBloc<SubjectEvent, SubjectState> implements SubjectBloc {}

/// Cubit Mock 클래스
class MockSubjectCubit extends MockCubit<SubjectState> implements SubjectCubit {}


/// Counter 추상 클래스
abstract class CounterEvent {}

/// Counter 추가 이벤트
class CounterIncrementPressed extends CounterEvent {}

/// 테스트할 Counter bloc
class CounterBloc extends Bloc<CounterEvent, int> {
  CounterBloc() : super(0) {
    on<CounterIncrementPressed>((event, emit) => emit(state + 1));
  }
}

첫번째로 해야할 일은 테스트할 bloc나 cubit을 위한 Mock 클래스를 작성해 주는 것입니다.
Mock 이란 간단히 말해 테스트용 스텁을 만들어주는 패키지인데, 실제 클래스 대신 테스트할때 도움을 주는 클래스입니다.
상세한 설명은 mokito 패키지 [https://pub.dev/packages/mockito]를 참고하시길 바랍니다.

2. blocTest를 활용한 방법 

bloc 패키지를 test 하기 위해서 bloc_test를 추가하여 bloc의 현재상태가 X일때, 새로운 이벤트 O가 입력되면 기대값 Y가 나오는지 확인하여 각 bloc의 유닛 테스트가 이루어지도록 코드를 작성할 수 있습니다.


void main() {
  /// 테스트 그룹
  group('CounterBloc', () {
    /// CounterBloc()를 build하고 초기값 확인
    blocTest(
      'emits [] when nothing is added',
      build: () => CounterBloc(),
      expect: () => [],
    );

    /// act: 이벤트 추가
    ///
    /// CounterBloc()를 build하고,
    /// bloc에 CounterIncrementPressed 이벤트가 추가되었을때 emit 테스트
    blocTest(
      'emits [1] when CounterIncrementPressed is added',
      build: () => CounterBloc(),
      act: (bloc) => bloc.add(CounterIncrementPressed()),
      expect: () => [1],
    );

    /// seed: 현재상태
    ///
    /// 현재값이 9 일때
    blocTest(
      'emits [10] when seeded with 9',
      build: () => CounterBloc(),
      seed: () => 9,
      act: (bloc) => bloc.add(CounterIncrementPressed()),
      expect: () => [10],
    );

    /// skip: 스트림을 스킵할 횟수
    ///
    /// CounterIncrementPressed() 가 두번 act 되었으므로
    /// 한번 건너뛴 상태값이 2인지 확인
    blocTest(
      'emits [2] when CounterIncrementPressed is added twice',
      build: () => CounterBloc(),
      act: (bloc) => bloc
        ..add(CounterIncrementPressed())
        ..add(CounterIncrementPressed()),
      skip: 1,
      expect: () => [2],
    );

    /// wait: async 함수일때 기다릴 시간
    blocTest(
      'emits [1] when CounterIncrementPressed is added',
      build: () => CounterBloc(),
      act: (bloc) => bloc.add(CounterIncrementPressed()),
      wait: const Duration(milliseconds: 300),
      expect: () => [1],
    );

    /// 검증하는 함수 추가
    blocTest(
      'emits [MyState] when MyEvent is added',
      build: () => CounterBloc(),
      act: (bloc) => bloc.add(CounterIncrementPressed()),
      expect: () => [1],
      verify: (_) {
        // verify(() => repository.someMethod(any())).called(1);
      },
    );

    /// errors : 잘못된 입력이 추가되었을때 
    /// 
    blocTest(
      'throws Exception when null is added',
      build: () => CounterBloc(),
      act: (bloc) => bloc.add(null),
      errors: () => [
        isA<Exception>(),
      ],
    );
  });
}

2-2. 테스트용 스트림을 활용한 방법 - 덤

더보기
/// Mock 클래스를 인스턴스화
final counterBloc = MockCounterBloc();

/// Mock Bloc의 초기 설정
whenListen(
  counterBloc,
  Stream.fromIterable([0, 1, 2, 3]),
  initialState: 0,
);

/// 초기설정 테스트
expect(counterBloc.state, equals(0));

/// 해당 bloc 스트림이 순차적으로 표현되는지 테스트
await expectLater(counterBloc.stream, emitsInOrder(<int>[0, 1, 2, 3]));

/// 마지막 상태값이 테스트용 스트림과 일치하는지 테스트
expect(counterBloc.state, equals(3));

 

댓글