Flutter业务逻辑管理插件bloc_presentation_test的使用

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

Flutter业务逻辑管理插件bloc_presentation_test的使用

bloc_presentation_test 是一个用于简化 BlocPresentationMixined Bloc/Cubit 测试的包。它与 bloc_presentation 包一起使用,以提供更直观的测试体验。

安装

首先,在项目的 pubspec.yaml 文件中添加 bloc_presentation_test 依赖项:

flutter pub add --dev bloc_presentation_test

使用方法

bloc_presentation_test 提供了两种方式来模拟 presentation 流:

  1. 使用 whenListenPresentation 函数
  2. 扩展目标 BlocPresentationMixined Bloc/CubitMockPresentationBloc/MockPresentationCubit

方法1 - whenListenPresentation

创建 Mock 类

首先,创建你的 BlocPresentationMixined Bloc/Cubit 的 mock 类。可以使用 bloc_test 包来实现这一点。

class MockCommentCubit extends MockCubit implements CommentCubit {}

获取 StreamController

然后,创建 MockCommentCubit 的实例,并通过调用 whenListenPresentation 来获取 StreamController

final mockCubit = MockCommentCubit();
final controller = whenListenPresentation(mockCubit);

这将模拟 MockCommentCubitpresentation 流,你可以订阅这个流。获取到的控制器可以用来向 presentation 流添加事件。

controller.add(const FailedToUpvote());

如果你指定了 initialEvents 参数,presentation 流将被模拟成给定事件的流。

final controller = whenListenPresentation(
  mockCubit,
  const [FailedToUpvote(), SuccessfulUpvote()],
);

返回的 StreamControllerCubit/Blocclose 方法中被释放。如果你重写了这个方法,则需要手动释放控制器。

方法2 - MockPresentationCubit/MockPresentationBloc

创建 Mock 类

首先,使用 MockPresentationBloc/MockPresentationCubit 创建你的 BlocPresentationMixined Bloc/Cubit 的 mock 类。

class MockCommentCubit extends MockPresentationCubit<CommentState> implements CommentCubit {}

发送模拟事件

然后,创建 MockCommentCubit 的实例并调用 emitMockPresentation

final mockCubit = MockCommentCubit();
mockCubit.emitMockPresentation(const FailedToUpvote());

这将向 presentation 流添加 FailedToUpvoteEvent 事件。

最后,记得调用 MockCommentCubitclose 方法。

示例代码

下面是一个完整的示例,展示了如何使用 bloc_presentation_test 进行测试。

import 'dart:async';

import 'package:bloc/bloc.dart';
import 'package:bloc_presentation/bloc_presentation.dart';
import 'package:bloc_presentation_test/bloc_presentation_test.dart';
import 'package:bloc_test/bloc_test.dart';
import 'package:test/test.dart';

// 定义 CounterCubit
class CounterCubit extends Cubit<int> with BlocPresentationMixin<int, CounterCubitEvent> {
  CounterCubit() : super(0);

  void count() {
    final newNumber = state + 1;

    if (state < 10) {
      emitPresentation(CounterPresentationEvent(newNumber));
    }

    emit(newNumber);
  }
}

// 定义 CounterCubitEvent
sealed class CounterCubitEvent {}

final class CounterPresentationEvent implements CounterCubitEvent {
  const CounterPresentationEvent(this.number);

  final int number;

  @override
  int get hashCode => number.hashCode;

  @override
  bool operator ==(Object other) {
    if (identical(this, other)) return true;
    return other is CounterPresentationEvent && other.number == number;
  }
}

// 定义 MockCounterPresentationCubit
class MockCounterPresentationCubit extends MockPresentationCubit<int, CounterCubitEvent> implements CounterCubit {}

// 定义 MockCounterCubit
class MockCounterCubit extends MockCubit<int> implements CounterCubit {}

void main() {
  mainMockPresentationCubit();
  mainWhenListenPresentation();
  mainBlocPresentationTest();
}

void mainMockPresentationCubit() {
  late MockCounterPresentationCubit mockCubit;

  setUp(() {
    mockCubit = MockCounterPresentationCubit();
  });

  tearDown(() {
    mockCubit.close();
  });

  test('presentation stream emits events properly', () async {
    mockCubit.emitMockPresentation(const CounterPresentationEvent(5));

    await expectLater(
      mockCubit.presentation,
      emitsInOrder([
        equals(const CounterPresentationEvent(5)),
      ]),
    );
  });
}

void mainWhenListenPresentation() {
  late MockCounterCubit mockCubit;
  late StreamController<CounterCubitEvent> controller;

  setUp(() {
    mockCubit = MockCounterCubit();
    controller = whenListenPresentation(
      mockCubit,
      initialEvents: [const CounterPresentationEvent(1)],
    );
  });

  tearDown(() {
    mockCubit.close();
  });

  test('presentation stream emits events properly', () async {
    controller.add(const CounterPresentationEvent(2));

    await expectLater(
      mockCubit.presentation,
      emitsInOrder([
        equals(const CounterPresentationEvent(1)),
        equals(const CounterPresentationEvent(2)),
      ]),
    );
  });
}

void mainBlocPresentationTest() {
  CounterCubit buildCubit() => CounterCubit();

  blocPresentationTest<CounterCubit, int, CounterCubitEvent>(
    'emits correct presentation event after calling count when state was smaller than 10',
    build: buildCubit,
    act: (cubit) => cubit.count(),
    expectPresentation: () => const [
      CounterPresentationEvent(1),
    ],
  );

  blocPresentationTest<CounterCubit, int, CounterCubitEvent>(
    'does not emit presentation event if count has not been called',
    build: buildCubit,
    expectPresentation: () => const <CounterPresentationEvent>[],
  );

  blocPresentationTest<CounterCubit, int, CounterCubitEvent>(
    'emits correct presentation event after calling count 2 times when state was smaller than 10',
    build: buildCubit,
    act: (cubit) => cubit..count()..count(),
    skipPresentation: 1,
    expectPresentation: () => const [
      CounterPresentationEvent(2),
    ],
  );

  blocPresentationTest<CounterCubit, int, CounterCubitEvent>(
    'does not emit presentation event after calling count when state was 10',
    build: buildCubit,
    seed: () => 10,
    act: (cubit) => cubit.count(),
    expectPresentation: () => const <CounterPresentationEvent>[],
  );
}

这个示例展示了如何使用 bloc_presentation_test 来测试带有 BlocPresentationMixinCubit。你可以根据需要调整和扩展这些测试用例,以适应你自己的业务逻辑。


更多关于Flutter业务逻辑管理插件bloc_presentation_test的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter业务逻辑管理插件bloc_presentation_test的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,关于Flutter业务逻辑管理插件bloc_presentation_test的使用,虽然这个特定的包名听起来像是用于测试Bloc模式的某种工具或框架的特定实现,但需要注意的是,在Flutter社区中,更常见的与Bloc模式相关的包是flutter_bloc,它用于状态管理和业务逻辑处理。flutter_bloc通常与bloc_test包一起使用来进行Bloc相关的测试。

不过,为了回应你的帖子,我将假设你提到的bloc_presentation_test是一个用于辅助测试Bloc逻辑的工具,并且你想要了解如何在Flutter项目中使用它来管理业务逻辑并进行测试。由于这个包的具体实现细节可能并不广为人知,我将提供一个基于flutter_blocbloc_test的示例,这通常是处理Flutter中Bloc逻辑和测试的标准方式。

1. 设置Flutter项目

首先,确保你的Flutter项目已经创建,并且在pubspec.yaml文件中添加了flutter_blocbloc_test依赖:

dependencies:
  flutter:
    sdk: flutter
  flutter_bloc: ^8.0.0 # 请检查最新版本

dev_dependencies:
  bloc_test: ^8.0.0 # 请检查最新版本
  test: any

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

2. 创建Bloc和事件/状态

定义一个简单的事件和状态类,以及一个处理这些事件的Bloc。

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

abstract class CounterEvent extends Equatable {
  const CounterEvent();

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

class IncrementEvent extends CounterEvent {}
class DecrementEvent extends CounterEvent {}

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

abstract class CounterState extends Equatable {
  const CounterState();

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

class CounterInitial extends CounterState {}
class CounterStateWithCount extends CounterState {
  final int count;

  const CounterStateWithCount({required this.count});

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

// counter_bloc.dart
import 'package:bloc/bloc.dart';
import 'events.dart';
import 'states.dart';

class CounterBloc extends Bloc<CounterEvent, CounterState> {
  CounterBloc() : super(CounterInitial());

  @override
  Stream<CounterState> mapEventToState(CounterEvent event) async* {
    if (event is IncrementEvent) {
      yield* _handleIncrementEvent();
    } else if (event is DecrementEvent) {
      yield* _handleDecrementEvent();
    }
  }

  Stream<CounterState> _handleIncrementEvent() async* {
    final currentState = state;
    if (currentState is CounterStateWithCount) {
      yield CounterStateWithCount(count: currentState.count + 1);
    }
  }

  Stream<CounterState> _handleDecrementEvent() async* {
    final currentState = state;
    if (currentState is CounterStateWithCount) {
      yield CounterStateWithCount(count: currentState.count - 1);
    }
  }
}

3. 使用BlocProvider在Flutter UI中提供Bloc

// main.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'counter_bloc.dart';
import 'counter_page.dart';

void main() {
  runApp(BlocProvider(
    create: (_) => CounterBloc(),
    child: MyApp(),
  ));
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: CounterPage(),
    );
  }
}

4. 创建UI组件并消费Bloc

// counter_page.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'counter_bloc.dart';

class CounterPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Counter')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            BlocBuilder<CounterBloc, CounterState>(
              builder: (context, state) {
                if (state is CounterStateWithCount) {
                  return Text(
                    'You have pushed the button this many times:',
                    style: Theme.of(context).textTheme.headline4,
                  );
                } else if (state is CounterInitial) {
                  return Text('0');
                } else {
                  return Text('?');
                }
              },
            ),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: () => context.read<CounterBloc>().add(IncrementEvent()),
              child: Text('Increment'),
            ),
            SizedBox(height: 10),
            ElevatedButton(
              onPressed: () => context.read<CounterBloc>().add(DecrementEvent()),
              child: Text('Decrement'),
            ),
          ],
        ),
      ),
    );
  }
}

5. 使用bloc_test进行单元测试

// counter_bloc_test.dart
import 'package:bloc_test/bloc_test.dart';
import 'package:test/test.dart';
import 'counter_bloc.dart';

void main() {
  group('CounterBloc', () {
    blocTest<CounterBloc, CounterState>(
      'emits [CounterStateWithCount(1)] when IncrementEvent is added',
      build: () => CounterBloc(),
      act: (bloc) => bloc.add(IncrementEvent()),
      expect: [CounterStateWithCount(count: 1)],
    );

    blocTest<CounterBloc, CounterState>(
      'emits [CounterStateWithCount(0), CounterStateWithCount(-1)] when DecrementEvent is added',
      build: () => CounterBloc()..add(IncrementEvent()),
      act: (bloc) => bloc.add(DecrementEvent()),
      expect: [
        CounterStateWithCount(count: 1),
        CounterStateWithCount(count: 0),
        CounterStateWithCount(count: -1),
      ],
      skip: 1, // Skip the initial state
    );
  });
}

这个示例展示了如何使用flutter_bloc进行状态管理,并使用bloc_test进行单元测试。如果你提到的bloc_presentation_test是一个特定的、不同的包,请提供更多信息以便给出更准确的示例。在大多数情况下,flutter_blocbloc_test是处理Flutter中Bloc逻辑和测试的标准方式。

回到顶部