skip to content

In NgRx, how do you test a component that shows the cart total from the store, using provideMockStore instead of real reducers?

level: middleimportance: must knowfreq 47%

answer

  1. replace the Store, not the reducers
  2. @ngrx/store/testing
  3. one selector, one fixed value
  4. dispatch changes nothing here
  5. spy on dispatch to assert intent

basics

~10 s

Provide provideMockStore from @ngrx/store/testing, pin the cart-total selector with overrideSelector or the selectors option, and assert the rendered total; since no reducers run, verify clicks by spying on store.dispatch.

solid answer

~40 s

`provideMockStore()` from `@ngrx/store/testing` swaps the real `Store` for a `MockStore` that has no reducers. I configure it with `initialState` for the whole state, and with `selectors: [{ selector: selectCartTotal, value: 45 }]` (or `store.overrideSelector(...)` after injecting `MockStore`) to pin exactly what the component reads. Both `store.select(selectCartTotal)` and `store.selectSignal(selectCartTotal)` then return 45, whatever the state. I assert the rendered total. Dispatching does not change the mock state, so to test the Pay button I spy on `store.dispatch` and expect the right action, or read `scannedActions$`. That test proves the component's wiring to the store, not the arithmetic; the selector and reducer have their own tests. And I call `store.resetSelectors()` in `afterEach`.

code

ts · 29 lines
ts
import { TestBed } from '@angular/core/testing';
import { MockStore, provideMockStore } from '@ngrx/store/testing';
import { CartSummaryComponent } from './cart-summary.component';
import { selectCartTotal } from './cart.selectors';
import { CheckoutPageActions } from './checkout.actions';

describe('CartSummaryComponent', () => {
  let store: MockStore;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [CartSummaryComponent],
      providers: [provideMockStore({ selectors: [{ selector: selectCartTotal, value: 45 }] })],
    });
    store = TestBed.inject(MockStore);
  });

  afterEach(() => store.resetSelectors());

  it('renders the total and dispatches placeOrderClicked on Pay', () => {
    const fixture = TestBed.createComponent(CartSummaryComponent);
    fixture.detectChanges();
    expect(fixture.nativeElement.textContent).toContain('45');

    const dispatch = vi.spyOn(store, 'dispatch');
    fixture.nativeElement.querySelector('button.pay').click();
    expect(dispatch).toHaveBeenCalledWith(CheckoutPageActions.placeOrderClicked());
  });
});

go deeper

for a junior

Recall the import path @ngrx/store/testing, the provideMockStore function, and that injecting MockStore gives you the same instance the component receives as Store.

for a middle

Explain the two ways to feed data, overrideSelector for derived values and initialState or setState for raw slices, and why dispatch must be asserted rather than its outcome.

for a senior

Keep suites isolated with resetSelectors in afterEach, and know when a mock store stops being honest and an integration test with provideStore is needed.

for a principal

Set a testing policy per layer: component tests pin selectors, selector and reducer tests cover logic, and a few integration tests cover the loop.

## Why mock the store for a component test An NgRx component usually does two things with the **store** (the single state container NgRx provides as the injectable `Store`): it **reads** state through selectors and it **dispatches** actions. A component test should prove exactly that wiring: the component shows what the selector returns, and a user gesture produces the right action. It should not re-prove the reducer's rules or the selector's arithmetic, which have cheaper tests of their own. Registering the real reducers would drag the whole feature into the test: you would have to dispatch a sequence of actions to reach "cart total is 45", and any bug in the reducer would fail the component test. `@ngrx/store/testing` offers a **mock store** instead. ## What provideMockStore gives you `provideMockStore(config)` returns providers that replace `Store` with `MockStore` (both tokens resolve to the same instance). The config has two optional fields: - `initialState` — the whole state object the mock starts with (defaults to `{}`); - `selectors` — a list of `{ selector, value }` pairs, each pinned to a fixed value. After injecting `MockStore`, you have more tools: | Member | Effect | |---|---| | `overrideSelector(selector, value)` | pins one memoized selector to a value, whatever the state | | `setState(nextState)` | replaces the whole mock state and emits it | | `refreshState()` | re-emits the current state so selectors re-evaluate | | `resetSelectors()` | removes every override this store applied | | `scannedActions$` | an observable of every dispatched action | Outside a `TestBed` the same store comes from `createMockStore(config)`. ## Reading: pin what the component reads For a cart summary that renders `store.selectSignal(selectCartTotal)`, the cleanest setup pins that one selector. Overriding works through the selector object itself, so it applies to both `store.select(selectCartTotal)` and `store.selectSignal(selectCartTotal)`: the selector returns the pinned value on every evaluation. The component renders 45 and the test asserts it. Choose between the two inputs deliberately: 1. **`overrideSelector`** when the component reads a derived value; the test does not need to know the state shape behind it. 2. **`initialState` / `setState`** when the component reads raw slices or several non-overridden selectors; you then own a realistic state shape. ## Writing: assert the intent, not the outcome `MockStore` has no reducers: its `addReducer` is a no-op and dispatched actions do not affect the state (the NgRx guide says so directly). So clicking Pay will not change any selector's output. The test asserts that the right action was dispatched: - spy on `store.dispatch` and expect `CheckoutPageActions.placeOrderClicked(...)`; or - subscribe to `store.scannedActions$`, which receives every dispatched action. Trying to read an updated order status after the click is the classic mistake: nothing will change, and the test either fails or, worse, asserts the pinned value and passes for the wrong reason. ## What this test proves, and hygiene A mock-store component test proves that the component reads the selectors it should and dispatches the actions it should. It does not prove that the total is computed correctly (the selector's projector test does), nor that `placeOrderClicked` leads to a charge (the effect test does). When you do want the pieces together, an **integration test** registers the real reducers with `provideStore` and `provideState` instead of the mock. One hygiene rule: overrides are stored on the selector objects, which are module-level singletons shared across the tests in a file. Call `store.resetSelectors()` in `afterEach` so a value pinned in one test cannot leak into the next. ## Common mistakes - **Asserting the outcome of a dispatch.** The mock store has no reducers, so a test that clicks Pay and then expects a changed total or status is testing nothing real. - **Pinning everything.** Overriding every selector the component touches can make the test a mirror of the template. Pin what the component derives; use `initialState` for simple raw reads. - **Forgetting the state shape.** A non-overridden selector still runs against the mock state. If `initialState` lacks the slice it reads, it may throw or return `undefined`, and the failure looks like a component bug. - **Mixing mock and real stores.** `provideMockStore` replaces `Store` entirely; registering real reducers alongside it has no effect, because the mock's reducer manager turns `addFeature` and `addReducer` into no-ops. - **Skipping cleanup.** Without `resetSelectors()`, a pinned value from one test can surface in the next and make results depend on test order.

  • Would you rather pin selectCartTotal or set a full initialState with cart items?
    Pin the selector when the component only renders the derived total: the test stays independent of the cart state's shape, and the selector's own test covers the arithmetic. Use `initialState` or `setState` when the component reads raw slices or several selectors you do not want to pin one by one, accepting that the test now depends on the state shape.
  • When is a mock store the wrong tool for a checkout component test?
    When the behaviour under test spans the loop, for example 'applying a coupon lowers the displayed total'. With a mock store nothing reacts to the dispatch, so you would be asserting your own pinned values. Register the real reducers with `provideStore` and `provideState` in an integration test, and mock only the edges such as the payment API.

saying these in an interview costs you the question

  • Dispatching on MockStore runs the feature reducer and updates the state.
  • overrideSelector affects store.select but not store.selectSignal.
  • provideMockStore needs the feature reducer registered with provideState first.
  • A mock-store component test proves the cart total is calculated correctly.
  • After clicking Pay, assert the new order status the mock store now holds.