Flutter副作用管理插件side_effect_cubit_test的使用
Flutter副作用管理插件side_effect_cubit_test的使用
本包简化了对产生副作用的Bloc进行测试的过程,它基于side_effect_cubit
包,增加了允许你期望某些副作用被发出的功能。
问题:在没有side_effect_cubit_test的情况下测试副作用
传统上,在Bloc中测试副作用是一个繁琐的过程。开发者必须:
- 手动收集副作用:创建一个列表来存储发出的副作用。
- 订阅副作用流:建立一个StreamSubscription来监听副作用事件。
- 将副作用添加到列表:在测试的构建步骤中,将接收到的副作用添加到之前创建的列表中。
- 断言副作用:在验证步骤中,验证副作用列表的内容。
- 清理:在tearDown步骤中取消StreamSubscription。
final receivedEffects = <RarOnboardingSideEffect>[];
StreamSubscription? effectSub;
blocTest<RarOnboardingBloc, RarOnboardingState>(
"should produce RarOnboardingEkycInitFailure when init ekyc plugin fail",
setUp: () {
when(() => rarRepository.getFaceAuthenConfig()).thenAnswer((_) async => FaceAuthenService.defaultConfig);
when(() => ekycPlugin.initialize(any())).thenAnswer((_) async => EKYCFailure());
},
build: () {
effectSub = bloc.sideEffects.asBroadcastStream().listen(receivedEffects.add);
return bloc;
},
act: (bloc) async => bloc.initConfig(),
verify: (bloc) {
expect(receivedEffects, [isA<RarOnboardingEkycInitFailure>()]);
},
tearDown: () => effectSub?.cancel()
);
这种方法引入了冗余,并使测试代码变得冗长,特别是当多个测试用例需要验证副作用时。
解决方案:side_effect_cubit_test
side_effect_cubit_test
提供了一种更简洁高效的方法来测试Bloc中的副作用。它利用现有的bloc_test
包,并引入了以下关键改进:
- 简化副作用预期:
expectSideEffects
函数允许你在测试设置中直接指定预期的副作用。这消除了手动收集副作用、管理订阅和单独验证步骤的需求。
sideEffectBlocTest<RarOnboardingBloc, RarOnboardingState, RarOnboardingSideEffect>(
"should produce RarOnboardingEkycInitFailure when init ekyc plugin fail",
setUp: () {
when(() => rarRepository.getFaceAuthenConfig()).thenAnswer((_) async => FaceAuthenService.defaultConfig);
when(() => ekycPlugin.initialize(any())).thenAnswer((_) async => EKYCFailure());
},
build: () => bloc,
act: (bloc) async => bloc.initConfig(),
expectSideEffects: () => [isA<RarOnboardingEkycInitFailure>()],
);
完整示例代码
以下是一个完整的示例代码,展示了如何使用side_effect_cubit_test
来测试带有副作用的Cubit。
import 'package:side_effect_cubit_test/side_effect_cubit_test.dart';
import 'package:side_effect_cubit/side_effect_cubit.dart';
import 'package:test/test.dart';
// 定义Cubit类
class CounterCubit extends SideEffectCubit<int, CounterSideEffect> {
CounterCubit() : super(0);
// 执行加一操作,并产生一个副作用
void plusOne() {
produceSideEffect(ShowInfoDialogEffect()); // 产生一个显示信息对话框的副作用
emit(state + 1); // 发射新的状态
}
}
// 定义副作用类型
abstract class CounterSideEffect {}
// 定义具体的副作用类型
class ShowInfoDialogEffect extends CounterSideEffect {}
void main() {
final counterCubit = CounterCubit();
// 定义测试组
group("side_effect tests", () {
// 使用sideEffectBlocTest进行测试
sideEffectBlocTest<CounterCubit, int, CounterSideEffect>(
"plusOne should yield ShowInfoDialogEffect", // 测试描述
build: () => counterCubit, // 构建Cubit实例
act: (cubit) => cubit.plusOne(), // 执行操作
expect: () => [1], // 预期状态
expectSideEffects: () => [isA<ShowInfoDialogEffect>()], // 预期副作用
);
});
}
更多关于Flutter副作用管理插件side_effect_cubit_test的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter副作用管理插件side_effect_cubit_test的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
side_effect_cubit_test
是一个用于管理 Flutter 应用中副作用的插件,通常与 Cubit
或 Bloc
状态管理库一起使用。它允许你在状态变化时执行副作用,而不会污染你的纯状态管理逻辑。
以下是如何使用 side_effect_cubit_test
的基本步骤:
1. 添加依赖
首先,你需要在 pubspec.yaml
文件中添加 side_effect_cubit
和 side_effect_cubit_test
依赖:
dependencies:
flutter:
sdk: flutter
side_effect_cubit: ^latest_version
dev_dependencies:
side_effect_cubit_test: ^latest_version
然后运行 flutter pub get
来获取依赖。
2. 创建 Cubit
假设你有一个 CounterCubit
,它管理一个计数器状态,并且在状态变化时触发副作用。
import 'package:side_effect_cubit/side_effect_cubit.dart';
class CounterCubit extends SideEffectCubit<int, String> {
CounterCubit() : super(0);
void increment() {
emit(state + 1);
emitSideEffect('Counter incremented to ${state + 1}');
}
void decrement() {
emit(state - 1);
emitSideEffect('Counter decremented to ${state - 1}');
}
}
在这个例子中,CounterCubit
继承了 SideEffectCubit
,它有两个泛型参数:int
表示状态类型,String
表示副作用类型。emitSideEffect
方法用于触发副作用。
3. 测试 Cubit
使用 side_effect_cubit_test
来测试 CounterCubit
的行为。
import 'package:flutter_test/flutter_test.dart';
import 'package:side_effect_cubit/side_effect_cubit.dart';
import 'package:side_effect_cubit_test/side_effect_cubit_test.dart';
import 'counter_cubit.dart';
void main() {
group('CounterCubit', () {
late CounterCubit counterCubit;
setUp(() {
counterCubit = CounterCubit();
});
tearDown(() {
counterCubit.close();
});
test('initial state is 0', () {
expect(counterCubit.state, 0);
});
sideEffectCubitTest<CounterCubit, int, String>(
'emits side effect when increment is called',
build: () => counterCubit,
act: (cubit) => cubit.increment(),
expectSideEffects: () => ['Counter incremented to 1'],
);
sideEffectCubitTest<CounterCubit, int, String>(
'emits side effect when decrement is called',
build: () => counterCubit,
act: (cubit) => cubit.decrement(),
expectSideEffects: () => ['Counter decremented to -1'],
);
});
}
在这个测试中,我们使用了 sideEffectCubitTest
来验证 CounterCubit
的状态变化和副作用。build
参数用于创建 Cubit
实例,act
参数用于执行操作,expectSideEffects
参数用于验证预期的副作用。
4. 在 UI 中使用 Cubit
在 Flutter UI 中,你可以使用 BlocBuilder
和 BlocListener
来监听状态和副作用。
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'counter_cubit.dart';
class CounterPage extends StatelessWidget {
[@override](/user/override)
Widget build(BuildContext context) {
return BlocProvider(
create: (_) => CounterCubit(),
child: CounterView(),
);
}
}
class CounterView extends StatelessWidget {
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Counter')),
body: BlocListener<CounterCubit, int, String>(
listener: (context, sideEffect) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(sideEffect)),
);
},
child: Center(
child: BlocBuilder<CounterCubit, int>(
builder: (context, state) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Count: $state'),
SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () => context.read<CounterCubit>().increment(),
child: Text('Increment'),
),
SizedBox(width: 20),
ElevatedButton(
onPressed: () => context.read<CounterCubit>().decrement(),
child: Text('Decrement'),
),
],
),
],
);
},
),
),
),
);
}
}