You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用blocTest对Flutter的CartItemBloc进行单元测试?

前置准备

首先确保你的CartItem模型类继承了Equatable,否则bloc测试无法正确比对两个CartItem实例是否相等,会导致预期状态匹配失败,示例CartItem定义参考:

import 'package:equatable/equatable.dart';

class CartItem extends Equatable {
  final String itemId;
  final double price;
  final int qty;

  const CartItem({
    required this.itemId,
    required this.price,
    this.qty = 1,
  });

  CartItem copyWith({
    String? itemId,
    double? price,
    int? qty,
  }) {
    return CartItem(
      itemId: itemId ?? this.itemId,
      price: price ?? this.price,
      qty: qty ?? this.qty,
    );
  }

  @override
  List<Object?> get props => [itemId, price, qty];
}

完整测试代码实现

import 'package:bloc_test/bloc_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:pikpo_screen/blocs/cart_item_bloc.dart';
import 'package:pikpo_screen/model/cart_item.dart';

void main() {
  // 预先定义模拟测试数据
  const mockItem1 = CartItem(itemId: "1", price: 35, qty: 1);
  const mockItem2 = CartItem(itemId: "2", price: 50, qty: 1);

  group('CartItemBloc 测试', () {
    late CartItemBloc cartItemBloc;
    late List<CartItem> initialItems;

    setUp(() {
      initialItems = [];
      cartItemBloc = CartItemBloc(initialItems);
    });

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

    test('初始状态为空列表', () {
      expect(cartItemBloc.state, equals([]));
    });

    blocTest<CartItemBloc, List<CartItem>>(
      '触发CartItemCheck事件时正确添加商品到列表',
      build: () => cartItemBloc,
      act: (bloc) => bloc.add(CartItemCheck(item: mockItem1)),
      expect: () => [
        [mockItem1]
      ],
    );

    blocTest<CartItemBloc, List<CartItem>>(
      '触发CartItemUncheck事件时正确移除对应商品',
      build: () => cartItemBloc,
      act: (bloc) async {
        // 先添加商品再移除
        bloc.add(const CartItemCheck(item: mockItem1));
        await Future.delayed(const Duration(milliseconds: 10));
        bloc.add(const CartItemUncheck(item: mockItem1));
      },
      expect: () => [
        [mockItem1],
        [],
      ],
    );

    blocTest<CartItemBloc, List<CartItem>>(
      '触发increment类型的CartItemUpdate事件时正确增加商品数量',
      build: () {
        // 初始状态自带mockItem1
        return CartItemBloc([mockItem1]);
      },
      act: (bloc) => bloc.add(const CartItemUpdate(itemId: "1", type: 'increment')),
      expect: () => [
        [mockItem1.copyWith(qty: 2)]
      ],
    );

    blocTest<CartItemBloc, List<CartItem>>(
      '触发decrement类型的CartItemUpdate事件时正确减少商品数量',
      build: () {
        // 初始状态自带数量为2的mockItem1
        return CartItemBloc([mockItem1.copyWith(qty: 2)]);
      },
      act: (bloc) => bloc.add(const CartItemUpdate(itemId: "1", type: 'decrement')),
      expect: () => [
        [mockItem1.copyWith(qty: 1)]
      ],
    );
  });
}

关键修改说明

  • blocTest需要明确指定泛型类型Bloc类和状态类型,避免类型匹配错误
  • build参数要直接返回初始化完成的Bloc实例,不要返回可空类型
  • 所有事件必须传入必填参数,比如CartItemCheck必须传入要添加的item参数
  • expect返回的状态序列要和事件触发后的状态变化顺序完全一致,且类型要和Bloc的状态类型(List<CartItem>)匹配
  • 测试完成后调用bloc.close()释放资源,避免内存泄漏

内容的提问来源于stack exchange,提问作者Raka Prasetyo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 20:18:02