new·The score now tells you which way it movedA brain's exam only ever grows: its own material writes questions, and so does every question a real caller asked and did not get answered. The score is a percentage over that growing set, so a brain that learned more could post a smaller number — and this week three did. One of them answered two MORE questions than the week before and showed eighteen points less. Printed as a single percentage, that reads as decline to a reader and as punishment to anyone who contributes material.all news →
mozg.beta
Sign in

Storybook · Writing and testing · all subjects

mocking

40 notes, read out of this brain and free to use. Each one was extracted from a source and is re-checked against its exam.

Mock Service Worker (MSW) for mocking network requests

MSW is an API mocking library that relies on service workers to capture network requests and provide mocked data in response. The MSW addon brings this functionality into Storybook, allowing you to mock API requests in your stories.

MSW addon version compatibility

The current instructions are for v3 of the MSW addon. When updating msw-storybook-addon to v3 from v2, you can migrate your configuration and stories automatically using the codemod: npx msw-storybook-migrate. For more information, see the MSW migration guide at https://github.com/mswjs/msw-storybook-addon/blob/main/MIGRATION.md#from-2xx-to-3xx.

Setting up MSW addon in Storybook

To set up the MSW addon: (1) Install MSW and the MSW addon using the appropriate npm command; (2) If not already using MSW, generate the service worker file necessary for MSW to work; (3) Ensure the staticDirs property in your Storybook configuration includes the generated service worker file (in /public, by default); (4) Initialize the addon and register it for all stories with a project-level loader (if using CSF 3) or by adding the addon to preview.ts (if using CSF Next).

Angular MSW addon setup consideration

Angular projects will likely need to adjust the command to save the mock service worker file in a different directory, such as src, rather than the default location.

Mocking REST API requests with MSW in stories

The MSW addon allows you to write stories that mock REST API requests. You can define request handlers for mocked GET, POST, and other HTTP methods. Each story can be configured using beforeEach to define the request handlers for the mock server.

Mocking GraphQL requests with MSW in stories

The MSW addon allows you to write stories that mock GraphQL requests. You can define request handlers for GraphQL queries and mutations. Each story can be configured using beforeEach to define the request handlers for the mock server. The example uses Apollo Client to make network requests, though other libraries like URQL or React Query can apply the same principles.

Configuring MSW handlers at different levels

You can use beforeEach to define request handlers for the mock server at the story level (for individual stories), at the component (meta) level (to apply handlers to all stories in a file), or at the project level (in preview.ts) to apply handlers to all stories in the project.

Using msw parameter in CSF 3

If using CSF 3, you can also use the msw parameter to define handlers for a story, component, or project, in addition to using beforeEach.

MSW supports multiple HTTP libraries

While the example uses the fetch API to make network requests, the same MSW principles can be applied to mock network requests in Storybook when using different libraries such as axios.

Three ways to mock modules in Storybook

Storybook provides three ways to mock modules for stories: automocking, subpath imports, and builder aliases. Automocking is the most straightforward approach and is recommended for projects using Vite and Webpack builders. The other two methods require more setup but provide similar functionality.

Automocking with sb.mock utility

Automocking uses the `sb.mock` utility function to register modules you want to mock. You register modules in your project-level configuration at `.storybook/preview.*` to ensure consistent and performant mocking across all stories. You can register both local modules (e.g., `../lib/session.ts`) and packages in `node_modules` (e.g., `uuid`).

Path requirements for local module mocking

When registering a mock for a local module, the path must not use an alias or subpath import (e.g., `@/lib/session.ts` or `#lib/session`), must be relative to the `.storybook/preview.*` file, and must include the file extension (e.g., `.ts` or `.js`). If using TypeScript, you can wrap the module path in `import()` to ensure the module is correctly resolved and typed.

Spy-only mocking approach

Spy-only mocking is recommended for most cases. Set the `spy` option to `true` when registering a mocked module. This leaves the original module's functionality intact while still allowing you to modify the behavior if needed and make assertions in your tests.

Fully automocked modules behavior

Fully automocked modules are registered by setting the `spy` option to `false` (or omitting it, as that is the default). This automatically replaces all exports from the module with Vitest mock functions, allowing you to control their behavior and make assertions. However, the module is still evaluated along with its dependencies, so side effects will still occur.

Automatic restoration of fn mocks

It is not necessary to restore `fn()` mocks with the cleanup function, as Storybook will already do that automatically before rendering a story. See the `parameters.test.restoreMocks` API for more information.

Mock file structure and placement

Mock files should be placed in a `__mocks__` directory next to the module you want to mock and should export the same named exports as the original module. For example, to mock the `session` module in the `lib` directory, create a file named `session.js|ts` in the `lib/__mocks__` directory. For packages in `node_modules`, create a `__mocks__` directory in the root of your project.

Mock files must use JavaScript and ESModules

Mock files must be written with JavaScript (not TypeScript) using ESModules (not CJS). They must export the same named exports as the original module. If you want to mock a default export, you can use `export default` in the mock file.

Vitest mock function methods for mocked modules

Mocked functions created with the `sb.mock` utility are full Vitest mock functions. Common useful methods include: `mockReturnValue(value)` to set the return value; `mockResolvedValue(value)` to set the value a mocked async function resolves to; and `mockImplementation(fn)` to set a custom implementation for the mocked function.

Automocking in development mode

In development mode, automocking relies on Vite's module graph invalidation. When a mock is added, changed, or removed in `.storybook/preview.*` or the `__mocks__` directory, the plugin intelligently invalidates all affected modules and triggers a hot reload, providing a fast and interactive development experience.

Automocking build-time processing

During build, a Vite plugin called viteMockPlugin scans `.storybook/preview.*` for all `sb.mock()` calls. If a corresponding file is found in the top-level `__mocks__` directory, that file is loaded and transformed by Vite. If no `__mocks__` file is found, the original module's code is transformed at build-time to replace its exports with mocks or spies. All mocking decisions and transformations happen at build time, resulting in no performance penalty.

Mocking scope differences from Vitest

Storybook's automocking has key differences from Vitest: mocks are global and defined only in `.storybook/preview.*` (you cannot call `sb.mock()` inside individual story files); all mocking decisions are finalized at build time making the system robust but less dynamic; you can still control behavior at runtime within a play function or `beforeEach` hook; there is no `sb.unmock()` or equivalent as the module graph is fixed in a production build.

sb.mock does not accept factory functions

The `sb.mock()` API does not accept a factory function as its second argument (e.g., `sb.mock('path', () => ({...}))`). This is because all mocking decisions are resolved at build time, whereas factories are executed at runtime.

Subpath imports for mocking

Subpath imports allow you to define custom paths for modules in your project, which can be used to replace the original module with a mock file. They work with both Vite and Webpack builders. Each subpath must begin with `#` to differentiate it from a regular module path. The `#*` entry is a catch-all that maps all subpaths to the root directory.

Subpath import mock file requirements

When creating a mock file for subpath imports, it must import the original module using a relative import (using a subpath or alias import would result in it importing itself). It should re-export all exports from the original module, use the `fn` utility to mock any necessary functionality, use the `mockName` method to ensure the name is preserved when minified, and should not introduce side effects that could affect other tests or components.

Wrapping external modules for subpath imports

You cannot directly mock an external module like `uuid` or `node:fs`. Instead, wrap it in your own module, which you can mock like any other internal one. For example, create a file `lib/uuid.ts` that imports and re-exports from the external module, then create a mock file `lib/uuid.mock.ts` for the wrapper using the `fn` utility.

Subpath imports package.json configuration

Configure subpath imports by defining the `imports` property in your project's `package.json` file. This property maps the subpath to the actual file path. Each module's entry should include `storybook`, `test`, and `default` keys. The `storybook` value is used when loaded in Storybook, while the `default` value is used when loaded in your project. The `test` condition is also used within Storybook, allowing the same configuration in Storybook and other tests.

TypeScript moduleResolution for subpath imports

Subpath imports will only be correctly resolved and typed when the `moduleResolution` property is set to `'Bundler'`, `'NodeNext'`, or `'Node16'` in your TypeScript configuration. If you are currently using `'node'`, that is intended for projects using Node.js version older than v10.

TypeScript type safety for mocked functions

When writing stories in TypeScript with subpath imports, you must import your mock modules using the full mocked file name to have the functions correctly typed in your stories. You do not need to do this in your component files; that is what the subpath import is for.

Using mocked utility for type-safe mocks

If writing your stories in TypeScript, you can use the `mocked` utility from `storybook/test` to ensure that mocked functions are correctly typed in your stories. This utility is a type-safe wrapper around the Vitest `vi.mocked` function.

beforeEach for mock setup and cleanup

Use the asynchronous `beforeEach` function to perform setup before the story renders (e.g., configure the mock behavior). This function can be defined at the story, component (which runs for all stories in the file), or project level (in `.storybook/preview.*`, which runs for all stories in the project). You can return a cleanup function from `beforeEach` which will be called after your story unmounts.

Webpack automocking ESM requirement

If you are using the Webpack builder, you can only automock `node_module` packages that have ESModules (ESM) entry points. If a module has both CommonJS (CJS) and ESM entry points, Webpack doesn't correctly resolve the ESM entry and it cannot be mocked. Webpack users can still mock CJS `node_module` packages by providing a mock file.

exports is not defined error with Webpack

Webpack projects may encounter an 'exports is not defined' error when using automocking. This is usually caused by attempting to mock a module with CommonJS (CJS) entry points. Automocking with Webpack only works with modules that have ESModules (ESM) entry points exclusively, so you must use a mock file to mock CJS modules.

Builder aliases for module mocking

If your project is unable to use automocking or subpath imports, you can configure your Storybook builder to alias the module to a mock file. This instructs the builder to replace the module with the mock file when bundling your Storybook stories. Usage of the aliased module in stories is similar to subpath imports, but you import the module using the alias instead of the subpath.

Mocking conflicts with other testing tools

If you have already set up mocking with other testing tools (e.g., Jest), you may encounter conflicts when using Storybook's mocking system. These conflicts can cause unexpected behavior, errors, or incorrect mocks when both tools try to mock the same module. To address this, verify which tool is responsible for mocking a particular module and ensure the configurations do not overlap.

Mock files for deeper import paths

If you need to mock an external module that has a deeper import path (e.g., `lodash-es/add`), register the mock with that path in automocking, or create a corresponding mock file (e.g., `__mocks__/lodash-es/add.js`) in the root of your project when using mock files.

Root __mocks__ directory in Vite projects

In Vite projects, the root `__mocks__` directory should be placed in the `root` directory as defined in your project's Vite configuration (typically `process.cwd()`). If that is unavailable, it defaults to the directory containing your `.storybook` directory.

Root __mocks__ directory in Webpack projects

In Webpack projects, the root `__mocks__` directory should be placed in the `context` directory as defined in your project's Webpack configuration (typically `process.cwd()`). If that is unavailable, it defaults to the root of your repository.

Mocking external dependencies in Storybook

To mock imports in Storybook, register module mocks in .storybook/preview.ts using sb.mock() with the spy option. Example: sb.mock(import('some-library'), { spy: true });. Use file extensions when referring to relative files: sb.mock(import('./relative/module.ts'), { spy: true });

Specify mock values per-story using beforeEach

Override mock behavior per-story using beforeEach and the mocked() type function. Import mocked from 'storybook/test' and use it to set mock values in the beforeEach hook before the story runs.

Mock imports example

Example of mocking and using mocked values in a story: import { expect, mocked, fn } from 'storybook/test'; import { library } from 'some-library'; const meta = { component: AuthButton, beforeEach: async () => { mocked(library).mockResolvedValue({ user: 'data' }); } }; export const LoggedIn: Story = { play: async ({ canvas }) => { await expect(library).toHaveBeenCalled(); } };

Give your agent this brain