У меня есть @Effect
, который использует MemoizedSelector
, чтобы взять предмет из магазина с избыточностью и mergeMap
с полезной нагрузкой действия.Эффект работает просто отлично, однако настройка Jest-тестов для этого оказалась трудной, поскольку я не могу посмеяться над возвращаемым значением селектора, поскольку select
является объявленной функцией, которая импортируется (из '@ ngrx / store') и используется в эффекте, а сам селектор также является импортированной функцией.Теперь я цепляюсь за соломинку.
Как мне написать модульный тест для проверки эффекта NGRX, использующего селектор магазина?
"@ ngrx / store": "^ 7.4.0 ",
" rxjs ":" ^ 6.2.2 "
Я пробовал следующие виды решений:
- использование
provideMockStore({
initialState
})
provideMockStore происходит из '@ngrx/store/testing';
, где начальным состоянием было и мое фактическое initialState, и состояние, которое содержит точную структуру / элементЯ пытаюсь выбрать
с использованием различных типов MockStore
из различных SO вопросов / ответов, а также различных подходов к публикациям в блогах
попытка издеваться над селекторомиспользуя <selector>.projector(<my-mock-object>)
(здесь мы понимаем, что это так, и я уверен, что это будет использовано при изолированном тестировании селектора , а не эффекта)
самого эффекта:
@Effect()
getReviewsSuccess$ = this.actions$.pipe(
ofType<ProductActions.GetReviewsSuccess>(
ProductActions.ProductActionTypes.GET_REVIEWS_SUCCESS
),
mergeMap(() => this.reduxStore.pipe(select(selectProduct))),
map(({ product_id }) => product_id),
map(product_id => new ProductActions.GetReviewsMeta({
product_id,
}))
);
Спецификация:
......
let effects: ProductEffects;
let facade: Facade;
let actions$: Observable<any>;
let store$: Observable<State>;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
RouterTestingModule,
// ^ I've also tried using StoreModule.forRoot(...) here to configure
// it in similar fashion to the module where this effect lives
],
providers: [
ProductEffects,
provideMockActions(() => actions$),
{
provide: Facade,
useValue: facadeServiceMock,
},
ResponseService,
provideMockStore({
initialState
})
// ^ also tried setting up the test with different variations of initialState
],
});
......
it('should return a GetReviewsMeta on successful GetReviewsSuccess', () => {
const reviews = {...reviewListMock};
const { product_id } = {...productMockFull};
const action = new ProductActions.GetReviewsSuccess({
reviews
});
const outcome = new ProductActions.GetReviewsMeta({
product_id
});
actions$ = hot('-a', { a: action });
// store$ = cold('-c', { c: product_id });
// not sure what, if anything I need to do here to mock select(selectProduct)
const expected = cold('-b', { b: outcome });
expect(effects.getReviewsSuccess$).toBeObservable(expected);
});
Селектор selectProduct
:
export const getProduct = ({product}: fromProducts.State) => product;
export const getProductState = createFeatureSelector<
fromProducts.State
>('product');
export const selectProduct = createSelector(
getProductState,
getProduct,
);
Я ожидаю, что тест пройден, но вместо этого я получаю следующееошибка
● Product Effects › should return a GetReviewsMeta on successful GetReviewsSuccess
expect(received).toBeNotifications(expected)
Expected notifications to be:
[{"frame": 10, "notification": {"error": undefined, "hasValue": true, "kind": "N", "value": {"payload": {"product_id": 2521}, "type": "[Reviews] Get Reviews Meta"}}}]
But got:
[{"frame": 10, "notification": {"error": [TypeError: Cannot read property 'product_id' of undefined], "hasValue": false, "kind": "E", "value": undefined}}]
Ясно, что MemoizedSelector
(selectProduct) не знает, что такое объект Product, который должен находиться в магазине (но, похоже, не вводит ли я initialState
, который имеетэто или нет), и я не могу получить product_id
Продукта, потому что я неправильно настроил его в beforeEach
или в самой спецификации ...