如何在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
相关产品推荐
相关产品推荐

