基于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
相关产品推荐
相关产品推荐

