Fusion Framework Mocking
When to use
Use when a test needs deterministic Fusion Framework module state — a signed-in user, a
current context, seeded feature flags/bookmarks, or faked HTTP responses — without a real
Entra ID tenant, service registry, or backend.
Typical triggers:
- "How do I mock
useAccessToken / sign in a test user?"
- "Seed a current context for this test"
- "Fake this HTTP endpoint's response"
- "Mock service discovery so the app doesn't hit the network"
- "Add a mock for my own module"
- "Test signed-out / login / logout behavior"
- "Assert a middleware was called"
Implicit triggers:
- A test imports
mockFramework, mockAppModules, or any @equinor/fusion-framework-module-*/mock entry point
- A test needs the app to boot without real credentials or a running backend
- Writing or reviewing a
*.mock.ts/mock-configurator file for a Fusion module
When not to use
- Rendering a component/hook or configuring Vitest/Browser Mode — use
fusion-framework-testing
dev-server.config.ts API mocking/proxying for local ffc app dev — that is dev-time only, unrelated to test-time module mocks; see fusion-developer-app
- Asserting on one specific call (arguments, call count, reset) — that is the test runner's job (
vi.spyOn, vi.fn); this skill only covers seeding module state
- Backend/service-repo test doubles (different repo, different stack)
Required inputs
Mandatory
- Which boundary needs faking: auth, service discovery, context, bookmarks, feature flags, HTTP, analytics, telemetry, app manifest, or a custom module
- Test scope: parent framework (
mockFramework), an app's own modules (mockAppModules), or a bespoke module graph (a module's own enable*Mock)
Conditional
- Signed-in user details (name, username, specific token claims) when auth matters
- Whether the test must stay fully offline (every expected request needs an answering middleware)
- Whether an OpenAPI document already describes the API under test
Instructions
Step 1 — Understand the mocking design
Read references/framework-and-app-mocks.md for the design rules before writing a mock:
- Only the client (the object doing network I/O) is substituted. Providers, configurators,
schema validation, and module
initialize all run for real — a test still catches wiring
mistakes.
- Mocks live beside the module they replace, at that module's own
/mock entry point.
@equinor/fusion-framework/mock only composes the built-in set; it contains no mock logic
of its own.
- There is no Fusion mocking API for individual calls. Mock clients are plain classes with
ordinary methods — use
vi.spyOn, bun:test's spyOn, or Node's t.mock.method directly.
Step 2 — Pick the right layer
| Test scope |
Use |
Import |
| Parent framework / portal-level modules together |
mockFramework |
@equinor/fusion-framework/mock |
| An app's own modules (no React) |
mockAppModules |
@equinor/fusion-framework-app/mock |
| One module in a bespoke module graph |
that module's enable*Mock |
@equinor/fusion-framework-module-*/mock |
| React hooks/components consuming app modules |
render helpers (out of scope here) |
see fusion-framework-testing |
import { mockFramework } from '@equinor/fusion-framework/mock';
const fusion = await mockFramework((configurator) => {
configurator.msal.setAccount({ name: 'Ada Lovelace' });
configurator.context.setCurrentContext({ id: 'project-a', type: { id: 'ProjectMaster' }, value: {} });
configurator.serviceDiscovery.addService({ key: 'catalog' });
});
mockFramework initializes every module FrameworkConfigurator declares (event, auth,
http, serviceDiscovery, context, telemetry) with zero configuration — a default signed-in
Test User and offline service resolution out of the box.
Step 3 — Seed the module the test needs
Use references/module-mocks.md for the full per-module table (auth, service discovery, context,
bookmark, feature flag, analytics, telemetry, app manifest) with defaults, builder methods, and
signed-out/token-exact recipes.
Step 4 — Fake HTTP responses
Use references/http-and-openapi-mocks.md to choose between a hand-written
configurator.http.addMiddleware(...), createRouterMiddleware for several routes, or
createOpenApiMockMiddleware (backed by @equinor/fusion-openapi-mock) for a whole OpenAPI
document. An unmatched request always falls through to next(uri, init) and eventually the real
network — a fully offline test must answer every request it expects.
Step 5 — Add a mock for a custom module (when needed)
Use references/custom-module-mocks.md when the module under test is not one of the built-ins —
covers the client seam (setClient), builder-owned configuration, and exposing a
configurator.<yourModule> accessor via FrameworkMockConfigurator's _pin/_getConfig.
Step 6 — Verify
Expected output
- Test code that seeds Fusion module state through the correct
/mock entry point
- A stated choice of layer (
mockFramework / mockAppModules / module enable*Mock) and why
- HTTP faking strategy chosen (hand-written middleware, router, or OpenAPI) and its offline guarantees
- Any custom-module mock wiring, if applicable
Safety & constraints
Never:
- Invent a Fusion-specific mocking API for individual call assertions — that is the test runner's job
- Treat an unsigned mock JWT (
createMockToken/MSAL mock output) as valid outside a test
- Replace a module's provider or configurator wholesale when only its client needs faking
- Assume
setResolveUnknownServices(false) is safe without declaring every service the framework itself resolves at start-up (e.g. context)
Always:
- Prefer the smallest mock boundary that still exercises the behavior under test
- Cite the real package/export used (e.g.
enableContextMock from @equinor/fusion-framework-module-context/mock) rather than inventing one
- Note when a test intentionally allows network fallthrough vs. when it must stay fully offline
1---2name: fusion-framework-mocking3description: Guides seeding deterministic Fusion Framework module state in tests — mockFramework, mockAppModules, and module-owned enableXMock helpers (msal, service-discovery, context, bookmark, feature-flag, analytics, telemetry, http, app) — while keeping each module's real configurator, provider, validation, and lifecycle. USE FOR: signing in a mock user, seeding context/bookmarks/feature-flags, faking HTTP/OpenAPI responses, choosing a mock boundary, adding a mock for a custom module. DO NOT USE FOR: configuring vitest.config.ts, rendering React components/hooks, or Vitest Browser Mode setup (use fusion-framework-testing); dev-server-time API mocking/proxying in dev-server.config.ts (use fusion-developer-app); backend/service-repo mocking.4license: MIT5---67# Fusion Framework Mocking89## When to use1011Use when a test needs deterministic Fusion Framework module state — a signed-in user, a12current context, seeded feature flags/bookmarks, or faked HTTP responses — without a real13Entra ID tenant, service registry, or backend.1415Typical triggers:16- "How do I mock `useAccessToken` / sign in a test user?"17- "Seed a current context for this test"18- "Fake this HTTP endpoint's response"19- "Mock service discovery so the app doesn't hit the network"20- "Add a mock for my own module"21- "Test signed-out / login / logout behavior"22- "Assert a middleware was called"2324Implicit triggers:25- A test imports `mockFramework`, `mockAppModules`, or any `@equinor/fusion-framework-module-*/mock` entry point26- A test needs the app to boot without real credentials or a running backend27- Writing or reviewing a `*.mock.ts`/mock-configurator file for a Fusion module2829## When not to use3031- **Rendering a component/hook or configuring Vitest/Browser Mode** — use `fusion-framework-testing`32- **`dev-server.config.ts` API mocking/proxying for local `ffc app dev`** — that is dev-time only, unrelated to test-time module mocks; see `fusion-developer-app`33- **Asserting on one specific call** (arguments, call count, reset) — that is the test runner's job (`vi.spyOn`, `vi.fn`); this skill only covers seeding module state34- Backend/service-repo test doubles (different repo, different stack)3536## Required inputs3738### Mandatory39- Which boundary needs faking: auth, service discovery, context, bookmarks, feature flags, HTTP, analytics, telemetry, app manifest, or a custom module40- Test scope: parent framework (`mockFramework`), an app's own modules (`mockAppModules`), or a bespoke module graph (a module's own `enable*Mock`)4142### Conditional43- Signed-in user details (name, username, specific token claims) when auth matters44- Whether the test must stay fully offline (every expected request needs an answering middleware)45- Whether an OpenAPI document already describes the API under test4647## Instructions4849### Step 1 — Understand the mocking design5051Read `references/framework-and-app-mocks.md` for the design rules before writing a mock:5253- Only the **client** (the object doing network I/O) is substituted. Providers, configurators,54 schema validation, and module `initialize` all run for real — a test still catches wiring55 mistakes.56- Mocks live beside the module they replace, at that module's own `/mock` entry point.57 `@equinor/fusion-framework/mock` only **composes** the built-in set; it contains no mock logic58 of its own.59- There is **no Fusion mocking API** for individual calls. Mock clients are plain classes with60 ordinary methods — use `vi.spyOn`, `bun:test`'s `spyOn`, or Node's `t.mock.method` directly.6162### Step 2 — Pick the right layer6364| Test scope | Use | Import |65| --- | --- | --- |66| Parent framework / portal-level modules together | `mockFramework` | `@equinor/fusion-framework/mock` |67| An app's own modules (no React) | `mockAppModules` | `@equinor/fusion-framework-app/mock` |68| One module in a bespoke module graph | that module's `enable*Mock` | `@equinor/fusion-framework-module-*/mock` |69| React hooks/components consuming app modules | render helpers (out of scope here) | see `fusion-framework-testing` |7071```typescript72import { mockFramework } from '@equinor/fusion-framework/mock';7374const fusion = await mockFramework((configurator) => {75 configurator.msal.setAccount({ name: 'Ada Lovelace' });76 configurator.context.setCurrentContext({ id: 'project-a', type: { id: 'ProjectMaster' }, value: {} });77 configurator.serviceDiscovery.addService({ key: 'catalog' });78});79```8081`mockFramework` initializes every module `FrameworkConfigurator` declares (`event`, `auth`,82`http`, `serviceDiscovery`, `context`, `telemetry`) with zero configuration — a default signed-in83`Test User` and offline service resolution out of the box.8485### Step 3 — Seed the module the test needs8687Use `references/module-mocks.md` for the full per-module table (auth, service discovery, context,88bookmark, feature flag, analytics, telemetry, app manifest) with defaults, builder methods, and89signed-out/token-exact recipes.9091### Step 4 — Fake HTTP responses9293Use `references/http-and-openapi-mocks.md` to choose between a hand-written94`configurator.http.addMiddleware(...)`, `createRouterMiddleware` for several routes, or95`createOpenApiMockMiddleware` (backed by `@equinor/fusion-openapi-mock`) for a whole OpenAPI96document. An unmatched request always falls through to `next(uri, init)` and eventually the real97network — a fully offline test must answer every request it expects.9899### Step 5 — Add a mock for a custom module (when needed)100101Use `references/custom-module-mocks.md` when the module under test is not one of the built-ins —102covers the client seam (`setClient`), builder-owned configuration, and exposing a103`configurator.<yourModule>` accessor via `FrameworkMockConfigurator`'s `_pin`/`_getConfig`.104105### Step 6 — Verify106107- [ ] The mocked boundary is the **client only** — configurator/provider/validation are real108- [ ] Determinism: tokens and resolved services are identical across runs (no `Date.now()`-derived assertions without seeding)109- [ ] Every expected HTTP request has an answering middleware, or the test intentionally allows fallthrough110- [ ] Call-level assertions use the test runner's own spy tooling, not an invented Fusion API111112## Expected output113114- Test code that seeds Fusion module state through the correct `/mock` entry point115- A stated choice of layer (`mockFramework` / `mockAppModules` / module `enable*Mock`) and why116- HTTP faking strategy chosen (hand-written middleware, router, or OpenAPI) and its offline guarantees117- Any custom-module mock wiring, if applicable118119## Safety & constraints120121Never:122- Invent a Fusion-specific mocking API for individual call assertions — that is the test runner's job123- Treat an unsigned mock JWT (`createMockToken`/MSAL mock output) as valid outside a test124- Replace a module's provider or configurator wholesale when only its client needs faking125- Assume `setResolveUnknownServices(false)` is safe without declaring every service the framework itself resolves at start-up (e.g. `context`)126127Always:128- Prefer the smallest mock boundary that still exercises the behavior under test129- Cite the real package/export used (e.g. `enableContextMock` from `@equinor/fusion-framework-module-context/mock`) rather than inventing one130- Note when a test intentionally allows network fallthrough vs. when it must stay fully offline