How to mock NgRx Signal Stores for unit tests and Storybook Play interaction tests (both manually and automatically)
Tags: angular, typescript, signals, javascript
Published: 2024-02-22 09:28:26

---
title: How to mock NgRx Signal Stores for unit tests and Storybook Play interaction tests (both manually and automatically)
published: true
tags: #angular #typescript #signals #javascript
canonical_url: https://www.angularaddicts.com/p/how-to-mock-ngrx-signal-stores
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/45hlqccytb3kki7ddnvr.png
---


In this article, I show you two techniques for mocking Signal Stores: 
- creating mock Signal Stores manually, and
- using the `provideMockSignalStore` function (it generates a mock version of a Signal Store) that’s designed to simplify the mocking process

Both of these techniques convert signals to writable signals, substitute functions with Sinon fakes, and replace `RxMethod`s with their fake counterparts in the Signal Store. These techniques together with [ng-mocks](https://ng-mocks.sudo.eu/) streamline the testing setup, enabling you to mock component dependencies (like services, stores, and child UI components) efficiently. I also cover how to apply this mocking approach in case of using custom store features or Storybook Play interaction tests.

So I'm going to show you how to unit test a component that uses a Signal Store using mock stores, and also an example on how to create a Storybook Story with a Storybook Play interaction test for the same component (again, using a mock store).
## Source code and demo app

The full source code of the mock signal store provider is available here: [`provideMockSignalStore`](https://github.com/gergelyszerovay/store-extensions/tree/main/projects/mock-signal-store/src)

Demo app [source](https://github.com/gergelyszerovay/store-extensions/tree/main/projects/demo/src/app/article-list-ngrx-signal-store):

- [Article list component](https://github.com/gergelyszerovay/store-extensions/blob/main/projects/demo/src/app/article-list-ngrx-signal-store/article-list-signal-store.component.ts), 
- [Unit test: Article list component + custom mock store](https://github.com/gergelyszerovay/store-extensions/blob/main/projects/demo/src/app/article-list-ngrx-signal-store/article-list-signal-store.component.mock-everything.spec.ts)
- [Unit test: Article list component + auto mocked store](https://github.com/gergelyszerovay/store-extensions/blob/main/projects/demo/src/app/article-list-ngrx-signal-store/article-list-signal-store.component.auto-mock-everything.spec.ts)
- [Storybook Play test: Article list component + auto mocked store](https://github.com/gergelyszerovay/store-extensions/blob/main/projects/demo/src/app/article-list-ngrx-signal-store/article-list-signal-store.component.stories.ts)

## Prerequisites

**To get the most out of this article, you should have a basic understanding of how Signals and Signal Store work:**

**Angular Signals** is a new reactivity model introduced in Angular 16. The Signals feature helps us track state changes in our applications and triggers optimized template rendering updates. If you are new to Signals, here are some highly recommended articles as a starting point:

* [Official Angular Signals documentation](https://angular.io/guide/signals)
* [“Signals in Angular – How to Write More Reactive Code”](https://www.freecodecamp.org/news/angular-signals/) by [Deborah Kurata](https://twitter.com/DeborahKurata)
* [“Angular & signals. Everything you need to know”](https://dev.to/this-is-angular/angular-signals-everything-you-need-to-know-2b7g) by [Robin Goetz](https://twitter.com/goetzrobin)

The NgRx team and [Marko Stanimirović](https://twitter.com/MarkoStDev) created a signal-based state management solution, [SignalStore](https://ngrx.io/guide/signals/signal-store). If you are new to Signal Stores, you should read [Manfred Steyer](https://twitter.com/ManfredSteyer)'s four-part series about the NgRx Signal Store:

* [The new NGRX Signal Store for Angular: 3+n Flavors](https://www.angulararchitects.io/en/blog/the-new-ngrx-signal-store-for-angular-2-1-flavors/)
* [Smarter, Not Harder: Simplifying your Application With NGRX Signal Store and Custom Features](https://www.angulararchitects.io/en/blog/smarter-not-harder-simplifying-your-application-with-ngrx-signal-store-and-custom-features/)
* [NGRX Signal Store Deep Dive: Flexible and Type-Safe Custom Extensions](https://www.angulararchitects.io/en/blog/ngrx-signal-store-deep-dive-flexible-and-type-safe-custom-extensions/)
* [The NGRX Signal Store and Your Architecture](https://www.angulararchitects.io/en/blog/the-ngrx-signal-store-and-your-architecture/)

It's also important to understand how [`RxMethod`s](https://ngrx.io/guide/signals/rxjs-integration#rxmethod) work.
## Article list component

In the demo app, we have the [ArticleListComponent_SS](https://github.com/gergelyszerovay/store-extensions/blob/main/projects/demo/src/app/article-list-ngrx-signal-store/article-list-signal-store.component.ts) component. It's a smart component and has a component level store, [ArticleListSignalStore](https://github.com/gergelyszerovay/mock-stores/blob/article-angular-libraries/projects/demo/src/app/article-list-ngrx-signal-store/article-list-signal-store.store.ts).

```typescript
@Component({
  selector: 'app-article-list-ss',
  standalone: true,
  changeDetection: ChangeDetectionStrategy.OnPush,
  imports: [UiArticleListComponent, UiPaginationComponent, HttpRequestStateErrorPipe],
  providers: [ArticleListSignalStore],
  templateUrl: 'article-list-signal-store.component.html',
})
export class ArticleListComponent_SS {
  // we get these from the router, as we use withComponentInputBinding()
  selectedPage = input<string | undefined>(undefined);
  pageSize = input<string | undefined>(undefined);

  HttpRequestStates = HttpRequestStates;

  readonly store = inject(ArticleListSignalStore);

  constructor() {
    LogSignalStoreState('ArticleListSignalStore', this.store);
    effect(() => {
      // 1️⃣ the effect() tracks these two signals only
      const selectedPage = this.selectedPage();
      const pageSize = this.pageSize();
      // 2️⃣ we wrap the function we want to execute on signal change
      // with an untracked() function
      untracked(() => {
        // we don't want to track anything in this block
        this.store.setSelectedPage(selectedPage);
        this.store.setPageSize(pageSize);
        this.store.loadArticles();
      });
      console.log('router inputs ➡️ store (effect)', selectedPage, pageSize);
    });
  }
}
```

```html
<h1 class="text-xl font-semibold my-4">SignalStore</h1>
<!-- 👇 Main UI state: initial / fetching 📡 -->
@if (
  store.httpRequestState() === HttpRequestStates.INITIAL ||
  store.httpRequestState() === HttpRequestStates.FETCHING
) {
  <div>Loading...</div>
}
<!-- 👇 Main UI state: fetched 📡 -->
@if (store.httpRequestState() === HttpRequestStates.FETCHED) {
  <!-- 👇 Article list UI component -->
  <app-ui-article-list [articles]="store.articles()" />
  <!-- 👇 Pagination UI component -->
  <app-ui-pagination
    [selectedPage]="store.pagination().selectedPage"
    [totalPages]="store.pagination().totalPages"
    (onPageSelected)="store.setSelectedPage($event); store.loadArticles()"
  />
}
<!-- 👇 Main UI state: error 📡 -->
@if (store.httpRequestState() | httpRequestStateErrorPipe; as errorMessage) {
  {{ errorMessage }}
}
```

The component has two signal inputs, it gets these from the router (we use `withComponentInputBinding()`):
- `selectedPage`
- `pageSize`

The component renders the following dumb/UI components, based on the state in the store:
- an article list component, and
- a pagination component 

The component updates the state in the Store, if one of the following things happen:
- the `selectedPage` or `pageSize` values change in the URL, or
- the user selects another page using the pagination component

### Signal Store for the Article list component

The store has the following state ([source code](https://github.com/gergelyszerovay/store-extensions/blob/article-angular-libraries/projects/demo/src/app/models/article-list.state.ts)):

```ts
export type ArticleListState = {
  readonly selectedPage: number,
  readonly pageSize: number,

  readonly httpRequestState: HttpRequestState,

  readonly articles: Articles,
  readonly articlesCount: number
}
```

And this is the Signal Store itself ([source code](https://github.com/gergelyszerovay/store-extensions/blob/article-angular-libraries/projects/demo/src/app/article-list-ngrx-signal-store/article-list-signal-store.store.ts)):

```ts
export const ArticleListSignalStore = signalStore(
  withState<ArticleListState>(initialArticleListState),
  withComputed(({ articlesCount, pageSize }) => ({
    totalPages: computed(() => Math.ceil(articlesCount() / pageSize())),
  })),
  withComputed(({ selectedPage, totalPages }) => ({
    pagination: computed(() => ({ selectedPage: selectedPage(), totalPages: totalPages() })),
  })),
  withMethods((store) => ({
    setSelectedPage(selectedPage: string | number | undefined): void {
      patchState(...);
    },
    setPageSize(pageSize: string | number | undefined): void {
      patchState(...);
    },
    setRequestStateLoading(): void {
      patchState(...);
    },
    setRequestStateSuccess(params: ArticlesResponseType): void {
      patchState(...);
    },
    setRequestStateError(error: string): void {
      patchState(...);
    },
  })),
  withMethods((store, articlesService = inject(ArticlesService)) => ({
    loadArticles: rxMethod<void>(
      pipe(...),
  })),
);
```

The store has the following state signals: `selectedPage: number`, `pageSize: number`, `httpRequestState: HttpRequestState`, `articles: Articles`, `articlesCount: number`.

It also has:
- computed selectors: `totalPages` and `withComputed`
- methods for updating its state: `setSelectedPage`, `setPageSize`, `setRequestStateLoading`, `setRequestStateSuccess`, `setRequestStateError`, and
- an `rxMethod` for fetching the article list from the `ArticlesService`: `loadArticles`

## Mocking Signal Stores

Let’s say we want to cover a smart component with unit tests, and the component contains complex logic, for example connections between app-, feature- and component level stores.

In this case, it can be useful to mock for instance services, stores, child components, all the things the component relies on.

I use the `MockComponent()` and `MockProvider()` functions from [ng-mocks](https://ng-mocks.sudo.eu/) for mocking components and plain services. 

`MockProvider()` doesn't work well with NgRx's `ComponentStore`s and `SignalStore`s, as it doesn't support `ComponentStore`'s `update()` and `effect()` function,  nor does it support `RxMethod`s. So we create a custom mock version of `ArticleListSignalStore`.  In order to do that, we replace:
 * `Signal`s by `WritableSignal`s
 * `Function`s by Sinon fakes
 * `RxMethod`s by `FakeRxMethod`s

This way we can explicitly set the selector signals' values in the unit test, and also check whether the functions and `RxMethod`s were called and with what parameters.

 `FakeRxMethod`s are generated by the `newFakeRxMethod()` function ([source code](https://github.com/gergelyszerovay/store-extensions/blob/main/projects/mock-signal-store/src/fake-rx-method.ts)). A `FakeRxMethod` is a function that accepts a static value, signal, or an observable as an input argument. It has a `FAKE_RX_METHOD` property, and contains a Sinon fake. This Sinon fake stores the call information in the following cases: If the FakeRxMethod was called 
 * with a static value
 * with a signal argument, and the signal's value changes
 * with an observable argument, and the observable emits

Here is a `TestBed` for the article list component ([source code](https://github.com/gergelyszerovay/store-extensions/blob/main/projects/demo/src/app/article-list-ngrx-signal-store/article-list-signal-store.component.mock-everything.spec.ts)), as you’ll see, the component's Signal Store and all the child UI components are mocked:

```typescript
class MockArticleListSignalStore {
  // Signals are replaced by WritableSignals
  selectedPage = signal(0);
  pageSize = signal(3);
  httpRequestState = signal<HttpRequestState>(HttpRequestStates.INITIAL);
  articles = signal<Articles>([]);
  articlesCount = signal(0);

  // Computed Signals are replaced by WritableSignals
  totalPages = signal(0);
  pagination = signal({ selectedPage: 0, totalPages: 0 });

  // Functions are replaced by Sinon fakes
  setSelectedPage = sinon.fake();
  setPageSize = sinon.fake();
  setRequestStateLoading = sinon.fake();
  setRequestStateSuccess = sinon.fake();
  setRequestStateError = sinon.fake();

  // RxMethods are replaced by FakeRxMethods
  loadArticles = newFakeRxMethod();
}

describe('ArticleListComponent_SS - mockComputedSignals: true + mock all child components', () => {
  let component: ArticleListComponent_SS;
  let fixture: ComponentFixture<ArticleListComponent_SS>;
  // we have to use UnwrapProvider<T> to get the real type of a SignalStore
  let store: UnwrapProvider<typeof ArticleListSignalStore>;

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      imports: [
        ArticleListComponent_SS,
        MockComponent(UiArticleListComponent),
        MockComponent(UiPaginationComponent),
      ],
      providers: [],
    })
      .overrideComponent(ArticleListComponent_SS, {
        set: {
          providers: [
            // override the component level providers
            MockProvider(ArticlesService), // injected in ArticleListSignalStore
            {
              provide: ArticleListSignalStore,
              useClass: MockArticleListSignalStore,
            },
          ],
        },
      })
      .compileComponents();

    fixture = TestBed.createComponent(ArticleListComponent_SS);
    component = fixture.componentInstance;
    // access to a service provided on the component level
    store = fixture.debugElement.injector.get(ArticleListSignalStore);
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });
});  
```

By using this approach, we manually create the `MockArticleListSignalStore`, and we have to update it each time we change the structure of the `ArticleListSignalStore`.

We get the type of the Signal Store with the `UnwrapProvider<typeof ArticleListSignalStore>`, as the `signalStore()` function returns a provider, not the store itself. 

We use the `overrideComponent()` function to override the component level providers of the article list component. We can get a store provided on the component level by `store = fixture.debugElement.injector.get(ArticleListSignalStore);`. This store is the mocked version of the original store.

Now it's time to write some unit tests. 

In the `constructor()` of the article list, we have an `effect()` that gets the `selectedPage()` and `pageSize()` signals from the router, updates the store by their values then calls the `store.loadArticles()` to fetch the articles from the server. So after we created the component in the `testBed` and ran the change detection with the `detectChanges()`, the effect and the `loadArticles()` should be executed:

```typescript
  describe('router inputs ➡️ store (effect)', () => {
    it("should update the store's state initially", () => {
      expect(getRxMethodFake(store.loadArticles).callCount).toBe(1);
    });
  });
```

`store.loadArticles` is a `FakeRxMethod`, and the `getRxMethodFake()` function returns the Sinon fake that stores the call information for `FakeRxMethod`.

We can also verify, that the effect runs `store.loadArticles()` if the `selectedPage()` input changes:

```typescript
  describe('router inputs ➡️ store (effect)', () => {
    it('should call loadArticles if the selectedPage router input changes', () => {
      getRxMethodFake(store.loadArticles).resetHistory();
      fixture.componentRef.setInput('selectedPage', '22');
      fixture.detectChanges(); // run the change detection to re-evaluate effects
      expect(getRxMethodFake(store.loadArticles).callCount).toBe(1);
    });
  });
```

We use `componentRef.setInput()` to change the component's input, as this method supports signal inputs, too. Then we run the `detectChanges()` to trigger the change detection that re-evaluates the effects. Finally, we expect that `store.loadArticles()` was called.

In the following test, we simulate a situation in which the article list is loaded, and we check that the article list is rendered and it gets the articles from the store:

```typescript
    describe('Main UI state: FETCHED', () => {
      let uiPaginationComponent: UiPaginationComponent;
      let uiArticleListComponent: UiArticleListComponent;
      beforeEach(() => {
        asWritableSignal(store.httpRequestState).set(HttpRequestStates.FETCHED);
        asWritableSignal(store.articles).set([
          { slug: 'slug 1', id: 1 } as Article,
        ]);
        asWritableSignal(store.pagination).set({
          totalPages: 4,
          selectedPage: 1,
        });
        fixture.detectChanges();

        uiArticleListComponent = fixture.debugElement.queryAll(
          By.directive(UiArticleListComponent)
        )[0]?.componentInstance as UiArticleListComponent;

        uiPaginationComponent = fixture.debugElement.queryAll(
          By.directive(UiPaginationComponent)
        )[0]?.componentInstance as UiPaginationComponent;
      });

      describe('Child component: article list', () => {
        it('should render the articles', () => {
          const uiArticleListComponent = fixture.debugElement.queryAll(
            By.directive(UiArticleListComponent)
          )[0]?.componentInstance as UiArticleListComponent;
          expect(uiArticleListComponent).toBeDefined();
          expect(uiArticleListComponent.articles).toEqual([
            { slug: 'slug 1', id: 1 } as Article,
          ] as Articles);
          expect(screen.queryByText(/loading/i)).toBeNull();
          expect(screen.queryByText(/error1/i)).toBeNull();
        });

        it('should get the article list from the store', () => {
          expect(uiArticleListComponent.articles).toEqual([
            { slug: 'slug 1', id: 1 } as Article,
          ] as Articles);
        });
      });
    });
```

In the `beforeEach()` function, we use the `asWritableSignal()` function to convert the type of the mocked store selector signals to `WritableSignal`. We set the values of these writable signals so that these simulate how the `setRequestStateSuccess()` stores the results of a HTTP request in the real store:
- `httpRequestState = HttpRequestStates.FETCHED`
- `articles` = `[{ slug: 'slug 1', id: 1 }]`
- `pagination = { totalPages: 4, selectedPage: 1, }`

Then we check whether the article list component is rendered or not, and whether it receives the proper `articles` input form the store.
## Auto-mocking Signal Stores

Creating mock stores manually is a time-consuming task, additionally, mock stores need to be updated every time when we change the original store's structure or initial values. It's also challenging to keep the types in sync in the mock and the real stores. Since the mock and the real store are two separate classes, Typescript can't support us.

To overcome these challenges, and automatically generate mock stores like the `MockArticleListSignalStore`, I created the `provideMockSignalStore()` function ([source code](https://github.com/gergelyszerovay/store-extensions/tree/main/projects/mock-signal-store/src)). It generates a mock version of a `SignalStore`, so it replaces:
 * `Signal`s by `WritableSignal`s
 * `Function`s by Sinon fakes
 * `RxMethod`s by `FakeRxMethod`s

We can use these mocked `SignalStore`s in unit tests, in Storybook and in Storybook Play tests.

Here is the updated `TestBed` for the article list component ([source code](https://github.com/gergelyszerovay/store-extensions/blob/main/projects/demo/src/app/article-list-ngrx-signal-store/article-list-signal-store.component.auto-mock-everything.spec.ts)), it uses `provideMockSignalStore()`. The component's Signal Store and all the child UI components are mocked:

```typescript
describe('ArticleListComponent_SS - mockComputedSignals: true + mock all child components', () => {
  let component: ArticleListComponent_SS;
  let fixture: ComponentFixture<ArticleListComponent_SS>;
  // we have to use UnwrapProvider<T> to get the real type of a SignalStore
  let store: UnwrapProvider<typeof ArticleListSignalStore>;
  let mockStore: MockSignalStore<typeof store>;

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      imports: [
        ArticleListComponent_SS,
        MockComponent(UiArticleListComponent),
        MockComponent(UiPaginationComponent),
      ],
      providers: [],
    })
      .overrideComponent(ArticleListComponent_SS, {
        set: {
          providers: [
            // override the component level providers
            MockProvider(ArticlesService), // injected in ArticleListSignalStore
            provideMockSignalStore(ArticleListSignalStore, {
              // if the mockComputedSignals is enabled (default),
              // you must provide an initial value for each computed signal
              initialComputedValues: {
                totalPages: 0,
                pagination: { selectedPage: 0, totalPages: 0 },
              },
            }),
          ],
        },
      })
      .compileComponents();

    fixture = TestBed.createComponent(ArticleListComponent_SS);
    component = fixture.componentInstance;
    // access to a service provided on the component level
    store = fixture.debugElement.injector.get(ArticleListSignalStore);
    mockStore = asMockSignalStore(store);
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });
});
```

 To access the store's spies and `FakeRxMethod`s in a type safe way, we create the `mockStore` alias for the store: `mockStore = asMockSignalStore(store);`, it has a `MockSignalStore<ArticleListSignalStore>` type.

We can pass the following options to  `provideMockSignalStore()`:
- `initialStatePatch`: A partial initial state, it overrides the original initial state
- `mockComputedSignals`: When `true`, replaces the computed signals by `WritableSignal`s (default is `true`). If we set this to `false`, `provideMockSignalStore()` keeps the original computed signals
 * `initialComputedValues`: Initial values for computed signals, we have to provide an initial value for each computed signal in the store, if `mockComputedSignals = true`
 * `mockMethods`: When `true`, replaces methods by Sinon fakes (default is `true`).
 * `mockRxMethods` When `true`, replaces `RxMethod`s by `FakeRxMethod`s  (default is `true`).


We can use the same unit tests we used with the `MockArticleListSignalStore`, and we can apply `patchState()` to update the state signals in the store:

```typescript
    describe('Main UI state: FETCHED', () => {
      let uiPaginationComponent: UiPaginationComponent;
      let uiArticleListComponent: UiArticleListComponent;
      beforeEach(() => {
        // this is the original code, still works:
        // asWritableSignal(store.httpRequestState).set(HttpRequestStates.FETCHED);
        // asWritableSignal(store.articles).set([
        //   { slug: 'slug 1', id: 1 } as Article,
        // ]);

        // simplified version with patchState:
        patchState(store, () => ({
          httpRequestState: HttpRequestStates.FETCHED,
          articles: [{ slug: 'slug 1', id: 1 } as Article],
        }));
        
        asWritableSignal(store.pagination).set({
          totalPages: 4,
          selectedPage: 1,
        });
        fixture.detectChanges();

        uiArticleListComponent = fixture.debugElement.queryAll(
          By.directive(UiArticleListComponent)
        )[0]?.componentInstance as UiArticleListComponent;

        uiPaginationComponent = fixture.debugElement.queryAll(
          By.directive(UiPaginationComponent)
        )[0]?.componentInstance as UiPaginationComponent;
      });
      describe('Child component: article list', () => {
        it('should get the article list from the store', () => {
          expect(uiArticleListComponent.articles).toEqual([
            { slug: 'slug 1', id: 1 } as Article,
          ] as Articles);
        });
      });

```

## Auto-mocking Signal Stores with Custom Store Features

Custom Store Features provide a mechanism to extend the functionality of SignalStores in a reusable way. They add state signals, computed signals, `RxMethod`s and methods to a Signal Store, so they are completely mockable with the `provideMockSignalStore()`. Here is an example test for an article list component, that uses a Signal Store together with the `withDataService` Custom Store Feature: [article-list-signal-store-feature.component.auto-mock-everything.spec.ts](https://github.com/gergelyszerovay/store-extensions/blob/main/projects/demo/src/app/article-list-ngrx-signal-store-feature/article-list-signal-store-feature.component.auto-mock-everything.spec.ts).

I wrote a detailed article on how the article list component together with the `withDataService` work: [Improve data service connectivity in Signal Stores using the withDataService Custom Store Feature](https://www.angularaddicts.com/p/signal-store-with-data-service)
## Auto-mocking Signal Stores in Storybook

You can also use mock Signal Stores generated by the `provideMockSignalStore()` in Storybook Stories and Storybook Play interaction tests ([source code](https://github.com/gergelyszerovay/store-extensions/blob/main/projects/demo/src/app/article-list-ngrx-signal-store/article-list-signal-store.component.stories.ts)):


```typescript
// https://github.com/storybookjs/storybook/issues/22352 [Bug]: Angular: Unable to override Component Providers
// We have to create a child class with a new @Component() decorator to override the component level providers

@Component({
  standalone: true,
  changeDetection: ChangeDetectionStrategy.OnPush,
  imports: [UiArticleListComponent, UiPaginationComponent, HttpRequestStateErrorPipe],
  // override the component level providers
  providers: [
    provideMockSignalStore(ArticleListSignalStore, {
      mockComputedSignals: false,
      initialStatePatch: {
        httpRequestState: HttpRequestStates.FETCHED,
        articles: [
          { id: 1, ... },
          { id: 2, ... }
        ],
        articlesCount: 8,
      },
    }),
  ],
  templateUrl: 'article-list-signal-store.component.html',
})
class ArticleListComponent_SS_SB extends ArticleListComponent_SS {}

const meta: Meta<ArticleListComponent_SS_SB> = {
  title: 'ArticleListComponent_SS',
  component: ArticleListComponent_SS_SB,
  decorators: [
    applicationConfig({
      // we can override root level providers here
      providers: [MockProvider(ArticlesService)],
    }),
  ],
  // ...
};

export default meta;
type Story = StoryObj<ArticleListComponent_SS_SB>;

export const Primary: Story = {
  name: 'Play test example',
  args: {},
  play: async ({ canvasElement }) => {
    const canvas = within(canvasElement);

    // get the component
    const componentEl = canvasElement.querySelector('ng-component');
    // @ts-ignore
    const component = ng.getComponent(componentEl) as ArticleListComponent_SS;

    // get the store as MockSignalStore
    const mockStore = asMockSignalStore(component.store);

    mockStore.setSelectedPage.resetHistory();
    getRxMethodFake(mockStore.loadArticles).resetHistory();

    const nav = within(await canvas.findByRole('navigation'));
    const buttons = await nav.findAllByRole('button');

    // the user clicks on page '2'
    // previous, 0, 1, 2 ...
    await userEvent.click(buttons[3]);
    await waitFor(() => {
      // loadArticles() should be called
      expect(getRxMethodFake(mockStore.loadArticles).callCount).toBe(1);
      // setSelectedPage(2) should be called
      expect(mockStore.setSelectedPage.callCount).toBe(1);
      expect(mockStore.setSelectedPage.lastCall.args).toEqual([2]);
    });
  },
};
```

Storybook currently has a limitation: it can't override component level providers, however it can override module and root level providers by setting the `Meta.decorators` and providing a new `applicationConfig()` ([GitHub issue](https://github.com/storybookjs/storybook/issues/22352)). To work around this limitation, we have to create a child class with a new `@Component()` decorator that contains the mock component-level providers.

## Summary

I hope that both my manual and automatic Signal Store mocking techniques will be useful for you. As I demonstrated in this article, these techniques (together with [ng-mocks](https://ng-mocks.sudo.eu/)) enable you to set up your tests more efficiently, for instance by mocking component dependencies more smoothly.   

Try out my `provideMockSignalStore` function, and please let me know how it worked out for you!

## 👨‍💻About the author

My name is [Gergely Szerovay](https://www.linkedin.com/in/gergelyszerovay/), I work as a frontend development chapter lead. Teaching (and learning) Angular is one of my passions. I consume content related to Angular on a daily basis — articles, podcasts, conference talks, you name it.

I created the Angular Addict Newsletter so that I can send you the best resources I come across each month. Whether you are a seasoned Angular Addict or a beginner, I got you covered.

Next to the newsletter, I also have a publication called [Angular Addicts](https://angularaddicts.com/). It is a collection of the resources I find most informative and interesting. Let me know if you would like to be included as a writer.

Let’s learn Angular together! [Subscribe here](https://www.angularaddicts.com/) 🔥

Follow me on [Substack](https://www.angularaddicts.com/), [Medium](https://medium.com/@GergelySzerovay), [Dev.to](https://dev.to/gergelyszerovay), [Twitter](https://twitter.com/GergelySzerovay) or [LinkedIn](https://www.linkedin.com/in/gergelyszerovay/) to learn more about Angular!