在单元测试中:如何覆盖由 entityAdapter.getSelectors() 创建的 NGRX 选择器
Posted
技术标签:
【中文标题】在单元测试中:如何覆盖由 entityAdapter.getSelectors() 创建的 NGRX 选择器【英文标题】:In unit test: How to override an NGRX selector which is created by entityAdapter.getSelectors() 【发布时间】:2021-11-29 18:41:48 【问题描述】:假设我们的应用有一个书籍页面。 我们正在使用:Angular、NGRX、jest。
提供上下文的一些代码行(见下面的实际问题):
图书页面状态接口:
export interface Book
bookID: string;
whatever: string;
export interface Books extends EntityState<Book>
error: boolean;
loaded: boolean;
export interface BooksFeature extends RootState
books: Books;
//...
作为功能添加到ngrx商店:
@NgModule(
imports: [
StoreModule.forFeature('books', booksReducer),
//...
]
ngrx entityAdapter 已创建:
export const booksAdapter = createEntityAdapter<Book>(
selectId: (book: Book): string => book.bookID,
);
从booksAdapter
的selectAll
创建booksSelector
const selectAll, selectTotal = booksAdapter.getSelectors((state: BooksFeature) => state.books);
export const booksSelector = selectAll;
将booksSelector
分配给组件属性:books$
public books$ = this.store.pipe(select(booksSelector));
然后,books$ 可观察对象用于许多事情(例如,<div *ngIf="(books$ | async).length">
等...)。
目标:假设如果books$
observable 始终与booksSelector
广播的值相同,我想单独进行单元测试。 p>
通常我会在组件的books.component.spec.ts
文件中执行以下操作:
组件测试的一般设置:
//...
describe('BooksComponent', () =>
let spectator: Spectator<BooksComponent>
let store: MockStore;
const initialState: RootState =
//...
books:
ids: [],
entities: ,
error: false,
loaded: false
;
const testBook: Book =
bookID: 'bookid_1',
whatever: 'whatever'
;
const createComponent = createComponentFactory(
component: BooksComponent,
providers: [
provideMockStore( initialState )
],
imports: [
StoreModule.forRoot(),
detectChanges: false
]
);
beforeEach(() =>
spectator = createComponent();
store = spectator.inject(MockStore);
);
afterEach(() =>
jest.clearAllMocks();
);
//...
还有重要的部分:
//...
describe('books$', () =>
it('books$ should have the same value as what booksSelector gives', () =>
const testBooks: Book[] = [testBook];
const expected = cold('a', a: testBooks );
const mockBooksSelector = store.overrideSelector(booksSelector, testBooks);
expect(spectator.component.books$).toBeObservable(expected);
);
//... Then I would like to use the mockBooksSelector.setResult(...) method too for other test cases
);
//...
问题在于MockStore
的overrideSelector
方法需要Selector
作为第一个参数,但entityAdapter
的getSelectors
方法返回一个selectAll
方法有不同的类型。
请告诉我如何用适当的解决方案替换此测试!
请记住,问题已被简化以保持重点,我不是在寻找这样的解决方案:
如果使用正确的选择调用 store.pipe,则改为测试。 手动更改状态以获得booksSelector
给出的期望值。
不仅在 .spec 文件中改变事物的解决方案。 (我的意思是,如果这真的是不可避免的,那么好吧)
谢谢!
【问题讨论】:
【参考方案1】:你需要使用特征选择器作为getSelectors
的输入:
const selectBookFeatureState =
createFeatureSelector<BooksFeature>('books');
const selectAll, selectTotal = booksAdapter.getSelectors(selectBookFeatureState)
如果这不起作用,您可以尝试创建这样的选择器:
const selectAllBooks = createSelector(
selectBookFeatureState,
selectAll
)
const selectTotalBooks = createSelector(
selectBookFeatureState,
selectTotal
)
并使用selectAllBooks
代替selectAll
和selectTotalBooks
代替selectTotal
【讨论】:
以上是关于在单元测试中:如何覆盖由 entityAdapter.getSelectors() 创建的 NGRX 选择器的主要内容,如果未能解决你的问题,请参考以下文章