Flutter状态管理测试插件bloc_test的使用

发布于 1周前 作者 songsunli 来自 Flutter

Flutter状态管理测试插件bloc_test的使用

简介

bloc_test 是一个用于简化 Bloc 和 Cubit 测试的 Dart 包。它与 blocmocktail 协同工作,使得编写和维护测试更加容易。

Bloc Test Package

Pub build codecov Star on Github Flutter Website Awesome Flutter Flutter Samples License: MIT Discord Bloc Library

更多信息请访问 bloclibrary.dev


创建 Mock 对象

为了方便测试,我们需要创建 Mock 对象来模拟真实的 Bloc 或 Cubit 行为。

import 'package:bloc_test/bloc_test.dart';

class MockCounterBloc extends MockBloc<CounterEvent, int> implements CounterBloc {}
class MockCounterCubit extends MockCubit<int> implements CounterCubit {}

模拟状态流

whenListen 方法可以用来模拟 Bloc 或 Cubit 的状态流。这对于返回预定义的状态序列非常有用。

// 创建一个 mock 实例
final counterBloc = MockCounterBloc();

// 模拟状态流
whenListen(
  counterBloc,
  Stream.fromIterable([0, 1, 2, 3]),
  initialState: 0,
);

// 断言初始状态是否正确
expect(counterBloc.state, equals(0));

// 断言模拟的状态流是否按预期发出
await expectLater(counterBloc.stream, emitsInOrder(<int>[0, 1, 2, 3]));

// 断言当前状态与模拟的状态流同步
expect(counterBloc.state, equals(3));

使用 blocTest 进行单元测试

blocTest 提供了一种简洁的方式来编写 Bloc 或 Cubit 的测试用例。它会自动处理断言 Bloc 发出的预期状态,并确保没有额外的状态被发出。

示例代码

定义 Cubit 和 Bloc

class CounterCubit extends Cubit<int> {
  CounterCubit() : super(0);

  void increment() => emit(state + 1);
}

abstract class CounterEvent {}

class CounterIncrementPressed extends CounterEvent {}

class CounterBloc extends Bloc<CounterEvent, int> {
  CounterBloc() : super(0) {
    on<CounterIncrementPressed>((event, emit) => emit(state + 1));
  }
}

编写测试用例

void main() {
  group('CounterCubit', () {
    blocTest<CounterCubit, int>(
      'emits [] when nothing is called',
      build: () => CounterCubit(),
      expect: () => const <int>[],
    );

    blocTest<CounterCubit, int>(
      'emits [1] when increment is called',
      build: () => CounterCubit(),
      act: (cubit) => cubit.increment(),
      expect: () => const <int>[1],
    );
  });

  group('CounterBloc', () {
    blocTest<CounterBloc, int>(
      'emits [] when nothing is added',
      build: () => CounterBloc(),
      expect: () => const <int>[],
    );

    blocTest<CounterBloc, int>(
      'emits [1] when CounterIncrementPressed is added',
      build: () => CounterBloc(),
      act: (bloc) => bloc.add(CounterIncrementPressed()),
      expect: () => const <int>[1],
    );
  });
}

使用 seed 设置初始状态

有时我们可能需要在测试开始前设置初始状态:

blocTest<CounterBloc, int>(
  'emits [10] when seeded with 9',
  build: () => CounterBloc(),
  seed: () => 9,
  act: (bloc) => bloc.add(CounterIncrementPressed()),
  expect: () => const <int>[10],
);

使用 skip 忽略部分状态

如果某些状态不需要断言,可以使用 skip 参数跳过它们:

blocTest<CounterBloc, int>(
  'emits [2] when CounterIncrementPressed is added twice',
  build: () => CounterBloc(),
  act: (bloc) => bloc..add(CounterIncrementPressed())..add(CounterIncrementPressed()),
  skip: 1,
  expect: () => const <int>[2],
);

处理异步操作

对于包含异步操作的 Bloc,可以使用 wait 参数等待一段时间再进行断言:

blocTest<MyBloc, MyState>(
  'emits [MyState] when MyEvent is added',
  build: () => MyBloc(),
  act: (bloc) => bloc.add(MyEvent()),
  wait: const Duration(milliseconds: 300),
  expect: () => [isA<MyState>()],
);

验证内部逻辑

可以使用 verify 参数来验证 Bloc 内部的方法调用:

blocTest<MyBloc, MyState>(
  'emits [MyState] when MyEvent is added',
  build: () => MyBloc(),
  act: (bloc) => bloc.add(MyEvent()),
  expect: () => [isA<MyState>()],
  verify: (_) {
    verify(() => repository.someMethod(any())).called(1);
  },
);

捕获异常

如果期望 Bloc 抛出异常,可以使用 errors 参数:

blocTest<MyBloc, MyState>(
  'throws Exception when null is added',
  build: () => MyBloc(),
  act: (bloc) => bloc.add(null),
  errors: () => [isA<Exception>()],
);

通过这些方法,我们可以更轻松地编写和维护 Bloc 和 Cubit 的测试用例,确保应用程序的状态管理逻辑是可靠的。


更多关于Flutter状态管理测试插件bloc_test的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter状态管理测试插件bloc_test的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是如何在Flutter项目中使用bloc_test进行状态管理测试的示例。bloc_test是一个用于测试Bloc状态管理的强大工具,它简化了对Bloc的单元测试过程。

首先,确保你的项目中已经添加了必要的依赖项。在pubspec.yaml文件中,你需要添加以下依赖项:

dependencies:
  flutter:
    sdk: flutter
  flutter_bloc: ^8.0.0  # 请根据最新版本调整
  bloc: ^8.0.0  # 请根据最新版本调整

dev_dependencies:
  flutter_test:
    sdk: flutter
  bloc_test: ^8.0.0  # 请根据最新版本调整

然后运行flutter pub get来安装这些依赖项。

接下来,我们假设你已经有一个简单的Bloc,比如一个计数器Bloc,它有两个事件:IncrementDecrement,以及两个状态:CounterState(初始状态)和CounterState(int count)(计数状态)。

定义CounterEvent和CounterState

import 'package:flutter_bloc/flutter_bloc.dart';

// 定义事件
enum CounterEvent {
  increment,
  decrement
}

// 定义状态
class CounterState {
  final int count;

  CounterState([this.count = 0]);

  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
      other is CounterState &&
          runtimeType == other.runtimeType &&
          count == other.count;

  @override
  int get hashCode => count.hashCode;
}

定义CounterBloc

import 'package:bloc/bloc.dart';

class CounterBloc extends Bloc<CounterEvent, CounterState> {
  CounterBloc() : super(CounterState()) {
    on<CounterEvent>((event, emit) {
      switch (event) {
        case CounterEvent.increment:
          emit(CounterState(state.count + 1));
          break;
        case CounterEvent.decrement:
          emit(CounterState(state.count - 1));
          break;
      }
    });
  }
}

使用bloc_test进行单元测试

import 'package:bloc_test/bloc_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:test/test.dart';

import 'counter_bloc.dart';

void main() {
  test('CounterBloc increments and decrements count', () {
    blocTest<CounterBloc, CounterState>(
      build: () => CounterBloc(),
      initialState: CounterState(),
      act: [
        CounterEvent.increment,
        CounterEvent.increment,
        CounterEvent.decrement,
      ],
      expect: [
        CounterState(1),
        CounterState(2),
        CounterState(1),
      ],
    );
  });
}

解释

  1. 依赖项:在pubspec.yaml文件中添加了flutter_blocblocbloc_test作为依赖项。

  2. 定义事件和状态CounterEvent是一个枚举,表示计数器的事件类型。CounterState是一个包含计数值的类。

  3. 定义BlocCounterBloc是一个继承自Bloc的类,它处理CounterEvent事件并发出CounterState状态。

  4. 单元测试:使用blocTest函数进行单元测试。build参数构建了一个CounterBloc实例,initialState参数定义了初始状态,act参数是一个事件列表,表示要触发的事件顺序,expect参数是一个状态列表,表示期望的状态变化顺序。

这个示例展示了如何使用bloc_test对Bloc进行简单的单元测试。通过这种方式,你可以确保你的Bloc在不同事件下的行为是符合预期的。

回到顶部