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

NgRx Signal Store测试咨询:模拟初始状态与withHooks测试方法

NgRx Signal Store 测试方案指南

目前NgRx Signal Store尚未提供官方测试文档,与@ngrx/store不同,相关测试方案需要自行探索。以下针对两个核心测试场景给出具体实现方案:

1. 模拟Store的初始状态

针对初始状态的模拟,最灵活的方案是将Signal Store封装为可接收初始状态参数的函数,默认使用业务预期的初始状态,测试时可传入自定义模拟状态:

封装可配置初始状态的Store

// 定义默认初始状态
const initialTaskState = {
  tasks: [],
  loading: false,
  error: null
};

// 封装Signal Store为工厂函数
export const createTaskStore = (initialState = initialTaskState) => 
  signalStore(
    { providedIn: 'root' },
    withState(initialState),
    withComputed((state) => ({
      completedTasks: computed(() => state.tasks().filter(t => t.completed))
    })),
    withMethods((state) => ({
      addTask: (task) => state.tasks.update(tasks => [...tasks, task])
    })),
    withHooks({
      onInit() {
        // 初始化逻辑,比如调用服务拉取数据
      }
    })
  );

测试中使用模拟初始状态

在测试用例中,直接传入自定义的模拟状态即可快速初始化Store:

describe('TaskStore Initial State', () => {
  it('should use mock initial state when provided', () => {
    const mockInitialState = {
      tasks: [{ id: 1, title: 'Mock Task', completed: false }],
      loading: true,
      error: 'Mock Error'
    };

    const testStore = createTaskStore(mockInitialState);

    expect(testStore.tasks()).toEqual(mockInitialState.tasks);
    expect(testStore.loading()).toBe(true);
    expect(testStore.error()).toBe('Mock Error');
  });

  it('should use default initial state when no mock is provided', () => {
    const testStore = createTaskStore();
    expect(testStore.tasks()).toEqual(initialTaskState.tasks);
  });
});

另外,也可以参考NgRx官方FAQ的类式Store实现,通过构造函数传入初始状态,但函数式工厂方案在测试中更轻便灵活。

2. 测试withHooks部分

withHooks中的onInit逻辑通常涉及初始化数据拉取、依赖服务调用等,以下两种方案可覆盖测试需求:

方案一:通过测试组件触发钩子

创建极简测试组件,注入Store后触发组件的ngOnInit,间接触发Store的onInit钩子,随后验证状态变化:

import { TestBed, ComponentFixture } from '@angular/core/testing';
import { Component, OnInit } from '@angular/core';

describe('TaskStore onInit Hook', () => {
  let fixture: ComponentFixture<TestComponent>;
  let testStore: ReturnType<typeof createTaskStore>;

  // 定义测试组件
  @Component({ template: '' })
  class TestComponent implements OnInit {
    constructor(public store: ReturnType<typeof createTaskStore>) {}
    ngOnInit() {}
  }

  beforeEach(() => {
    TestBed.configureTestingModule({
      declarations: [TestComponent],
      providers: [createTaskStore]
    });

    fixture = TestBed.createComponent(TestComponent);
    testStore = fixture.componentInstance.store;
  });

  it('should update state after onInit runs', () => {
    // 触发组件初始化,进而触发Store的onInit
    fixture.detectChanges();

    // 断言钩子执行后的状态
    expect(testStore.loading()).toBe(false);
    expect(testStore.tasks().length).toBeGreaterThan(0);
  });
});

方案二:模拟依赖服务验证钩子行为

如果onInit中调用了外部服务(如API请求),可通过模拟服务来验证钩子是否正确触发了服务方法:

import { TestBed } from '@angular/core/testing';
import { TaskService } from './task.service';

describe('TaskStore onInit Hook with Service', () => {
  let mockTaskService: jasmine.SpyObj<TaskService>;

  beforeEach(() => {
    // 创建模拟服务
    mockTaskService = jasmine.createSpyObj('TaskService', ['fetchTasks']);
    // 模拟服务返回值
    mockTaskService.fetchTasks.and.returnValue(of([{ id: 1, title: 'Fetched Task', completed: false }]));

    TestBed.configureTestingModule({
      providers: [
        createTaskStore,
        { provide: TaskService, useValue: mockTaskService }
      ]
    });
  });

  it('should call fetchTasks during onInit', () => {
    // 注入Store时,onInit会自动执行
    TestBed.inject(createTaskStore);

    // 断言服务方法被调用
    expect(mockTaskService.fetchTasks).toHaveBeenCalled();
  });
});

目前社区已有人提出NgRx Signal Store测试文档缺失的问题,相关讨论在NgRx平台的开源议题中。

内容的提问来源于Stack Exchange,提问作者Karol

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 20:02:20