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

基于RxJS的BLoC架构如何测试状态变更逻辑

基于RxJS的BLoC状态测试方案

核心问题

你当前测试失效的核心原因是直接覆盖了HomeScreenBloc.getCategories的原生实现,导致真正修改状态的逻辑完全没有执行,自然无法验证状态变更。另外你没有等待异步逻辑执行完成就读取状态,也会导致拿到的始终是初始值。

调整步骤

1. 调整BLoC导出逻辑(可选,推荐)

当前你直接导出BLoC单例,会导致不同测试用例之间状态互相污染,建议同时导出类和单例,方便测试时每次新建干净的实例:

// BLoC实现文件末尾修改导出逻辑
export class HomeScreenBloc extends ProtoBloc<IHomeScreenState> implements IHomeScreenService {
  // 原有实现保持不变
}
export default new HomeScreenBloc()

2. 正确mock依赖

你不需要测试API调用,因此只需要mock BLoC依赖的delivery层接口,不要修改BLoC自身的方法实现。

3. 编写正确的测试用例

所有异步BLoC方法调用需要加await等待执行完成,再读取最新状态做断言。

完整测试代码示例

import { HomeScreenBloc } from './index'
import delivery from 'delivery'

// 全局mock delivery模块
jest.mock('delivery', () => ({
  MealAPI: {
    getCategories: jest.fn(),
    getRecipesByCategory: jest.fn()
  }
}))

// 模拟数据
const mockCategories = [{ id: 1, name: 'Beef' }, { id: 2, name: 'Chicken' }]
const mockBeefRecipes = [{ id: '1', name: 'Beef Stew' }]

describe('HomeScreenBloc 测试', () => {
  let bloc: HomeScreenBloc
  const defaultState = {
    categoriesData: [],
    mealsData: null,
    isLoaded: false,
    mealsLoading: false,
    currentCategory: 'Beef'
  }

  // 每次测试前重置环境
  beforeEach(() => {
    bloc = new HomeScreenBloc()
    jest.clearAllMocks()
  })

  test('默认状态符合预期', () => {
    expect(bloc.state).toEqual(defaultState)
  })

  test('getCategories 请求成功时正确更新分类状态', async () => {
    // 模拟API返回成功
    (delivery.MealAPI.getCategories as jest.Mock).mockResolvedValue({
      value: { categories: mockCategories },
      error: null
    })

    await bloc.getCategories()

    expect(delivery.MealAPI.getCategories).toBeCalledTimes(1)
    expect(bloc.state.categoriesData).toEqual(mockCategories)
  })

  test('getCategories 请求失败时不修改原有状态', async () => {
    // 模拟API返回错误
    (delivery.MealAPI.getCategories as jest.Mock).mockResolvedValue({
      value: null,
      error: new Error('请求失败')
    })

    await bloc.getCategories()

    expect(bloc.state.categoriesData).toEqual([])
  })

  test('initialLoad 执行完成后所有初始状态更新正确', async () => {
    (delivery.MealAPI.getCategories as jest.Mock).mockResolvedValue({
      value: { categories: mockCategories },
      error: null
    })
    (delivery.MealAPI.getRecipesByCategory as jest.Mock).mockResolvedValue({
      value: { meals: mockBeefRecipes },
      error: null
    })

    await bloc.initialLoad()

    expect(delivery.MealAPI.getRecipesByCategory).toBeCalledWith('Beef')
    expect(bloc.state.mealsData).toEqual(mockBeefRecipes)
    expect(bloc.state.isLoaded).toBe(true)
  })
})

额外注意点

  • 如果不调整导出逻辑仍使用原有单例,需要在beforeEach中手动调用BLoC的状态重置方法,将状态恢复为初始值,避免用例间干扰
  • 如果你需要测试加载中等中间状态的变化,可以订阅ProtoBloc暴露的RxJS状态流,配合RxJS测试工具验证状态变更的时序
  • 所有异步方法调用必须添加await,否则读取状态时异步逻辑还未执行,拿到的会是旧状态

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 12:06:01