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

如何在NGXS测试中指定Mock特定的Store Selector?

问题描述

在Angular组件中定义了两个NGXS Selector:

@Component({
  selector: 'some-component',
  templateUrl: './some-component.html',
  styleUrls: ['./some-component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class OverviewComponent implements OnInit {

  @Select(State.foo) foo$: Observable<StateModel['foo']>;
  @Select(State.bar) bar$: Observable<StateModel['bar']>;

  ngOnInit(): void {
   
    combineLatest([foo$, bar$])
       .subscribe(([foo, bar]) => {
         this.bar = bar;
       });
  }

  // 其他代码省略

测试时通过mockReturnValueOnce按调用顺序Mock Store的select方法:

describe('SomeComponent', () => {
  beforeEach(async () => {

    stub.state = {}

    stub.Store = {
      select: jest.fn(),
    }

    await TestBed.configureTestingModule({
      declarations: [SomeComponent],
      imports: [NgxsModule.forRoot([])],
       providers: [{ provide: Store, useValue: stub.Store }]
    }).compileComponents();
  });

  it('does something', () => {
    jest.spyOn(stub.Store, 'select')
      .mockReturnValueOnce(of(false)); // <-- 对应@Select foo$的调用
      .mockReturnValueOnce(of(true)); // <-- 对应@Select bar$的调用

    expect(component.bar).toBe(true);
  });
});

当前这种按顺序Mock的方式无法清晰区分对应哪个Selector,随着测试用例扩展可读性会变差,请问有没有办法指定要Mock的具体Selector?

解决方案

1. 根据Selector参数精准Mock

利用jest的mockImplementation给select方法写自定义逻辑,根据传入的Selector返回对应值,完全摆脱调用顺序的依赖:

it('does something', () => {
  jest.spyOn(stub.Store, 'select').mockImplementation((selector) => {
    if (selector === State.foo) {
      return of(false);
    } else if (selector === State.bar) {
      return of(true);
    }
    return of(null); // 兜底返回,避免未覆盖的Selector触发异常
  });

  expect(component.bar).toBe(true);
});

2. 使用NGXS官方测试工具(推荐)

NGXS提供了NgxsTestingModule,可以直接Mock整个状态,不需要手动处理Store的select方法,更符合NGXS测试最佳实践:

import { NgxsTestingModule } from '@ngxs/testing';

describe('SomeComponent', () => {
  beforeEach(async () => {
    await TestBed.configureTestingModule({
      declarations: [SomeComponent],
      imports: [
        NgxsTestingModule.withStates([
          {
            name: 'yourStateName', // 替换为你的状态名称
            defaults: {
              foo: false,
              bar: true
            }
          }
        ])
      ]
    }).compileComponents();
  });

  it('does something', () => {
    // 组件会自动从Mock状态中获取对应Selector的值
    expect(component.bar).toBe(true);
  });
});

3. 给Selector添加标识区分(针对动态Selector)

如果你的Selector是函数类型(动态生成的),可以给函数添加唯一标识来区分:

// 在状态类中给Selector添加自定义标识
State.foo = (state: StateModel) => state.foo;
State.foo['selectorKey'] = 'fooSelector';

State.bar = (state: StateModel) => state.bar;
State.bar['selectorKey'] = 'barSelector';

// 测试中通过标识判断返回值
it('does something', () => {
  jest.spyOn(stub.Store, 'select').mockImplementation((selector) => {
    switch (selector['selectorKey']) {
      case 'fooSelector':
        return of(false);
      case 'barSelector':
        return of(true);
      default:
        throw new Error(`未处理的Selector: ${selector['selectorKey']}`);
    }
  });

  expect(component.bar).toBe(true);
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 11:22:42