Resource library

QA How-To

Mock React Context in Playwright Component Tests

Learn to mock react context playwright component tests with typed providers, reusable harnesses, nested values, isolation, and runnable permission examples.

18 min read | 3,821 words

TL;DR

To mock React context in Playwright component tests, mount the subject inside the real context provider and pass a deterministic value for the scenario. For repeated nested setup, create a typed test harness that composes production providers while keeping identity, permissions, and theme explicit.

Key Takeaways

  • Mount the component inside its real provider and control the provider value per test.
  • Use a small typed harness when several providers always travel together.
  • Keep scenario-defining values visible in each test instead of hiding them in global defaults.
  • Create fresh provider state for every mount so parallel tests cannot leak identity or permissions.
  • Assert user-visible behavior through roles and accessible names, not hook internals.
  • Test missing-provider behavior when the context contract requires a provider.

To mock react context playwright component tests reliably, wrap the component in its real React provider and pass a controlled value for each scenario. This exercises the production context contract in a real browser while avoiding a logged-in backend, global store, or complete application shell.

This tutorial builds an account menu whose visible actions depend on authentication, permissions, and theme. It complements the Playwright Component Testing for React complete guide, which explains where provider-driven tests fit among unit, API, and end-to-end coverage.

You will begin with one explicit provider, grow the setup into a reusable typed harness, verify nested providers and updates, and protect every test from shared state. The code uses TypeScript and public React and Playwright Component Testing APIs available in 2026.

What You Will Build

  • An AuthContext with an explicit provider contract and a safe custom hook.
  • An account menu that shows identity, permissions, and sign-out behavior.
  • Playwright component tests for anonymous, viewer, editor, and administrator states.
  • A typed TestProviders harness that composes authentication and theme context.
  • A stateful test host that proves consumers react when a provider value changes.
  • Isolation checks that stay deterministic during parallel and repeated execution.

The final tests operate through accessible text, buttons, and regions. They do not inspect React hooks, context objects, implementation classes, or private state.

Prerequisites

Use a supported Node.js LTS release, npm, React with TypeScript, and a current matching pair of @playwright/experimental-ct-react and @playwright/test. Playwright Component Testing remains experimental, so pin compatible packages through your lockfile and review release notes during upgrades.

Create a Vite project and initialize component testing:

npm create vite@latest context-ct-demo -- --template react-ts
cd context-ct-demo
npm install
npm init playwright@latest -- --ct

Choose React when prompted. If you already have a Vite React application, install the CT packages and browser directly:

npm install -D @playwright/experimental-ct-react @playwright/test
npx playwright install chromium

Add a script if initialization did not add one:

{
  "scripts": {
    "test:ct": "playwright test -c playwright-ct.config.ts"
  }
}

Your generated playwright-ct.config.ts should import defineConfig from @playwright/experimental-ct-react and point testDir at the directory that will contain the specs. The examples below use playwright/.

Step 1: Create a Strict React Authentication Context

Define the production contract first. A context default that looks like a real user can hide a missing provider, so use undefined and throw a precise error from the custom hook. This turns incorrect composition into an immediate, understandable failure.

// src/context/AuthContext.tsx
import { createContext, type ReactNode, useContext } from 'react';

export type Role = 'viewer' | 'editor' | 'admin';
export type AuthUser = {
  id: string;
  name: string;
  role: Role;
};

export type AuthContextValue = {
  user: AuthUser | null;
  signOut: () => void;
};

const AuthContext = createContext<AuthContextValue | undefined>(undefined);

export function AuthProvider({
  value,
  children,
}: {
  value: AuthContextValue;
  children: ReactNode;
}) {
  return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}

export function useAuth() {
  const value = useContext(AuthContext);
  if (value === undefined) {
    throw new Error('useAuth must be used inside AuthProvider');
  }
  return value;
}

Export the provider, types, and hook, but keep the raw context private. Application components get one supported access path. Tests will use the same provider as production, which catches provider-contract changes that a replaced hook module could conceal.

Do not place a network request in this provider merely to make the demo realistic. Context distributes state. The service that obtains identity can remain a separate dependency and deserves its own contract or integration coverage.

Verify the step: run npm run build. TypeScript should compile without an unsafe cast or implicit any. Temporarily call useAuth outside AuthProvider in a development component and confirm the error names the missing provider, then remove that temporary code.

Step 2: Build a Context-Dependent Account Menu

Create a component with meaningful branches. Anonymous users receive a sign-in link. Authenticated users see their name and role-specific actions. The component calls the context callback when the user signs out.

// src/components/AccountMenu.tsx
import { useAuth } from '../context/AuthContext';

export function AccountMenu() {
  const { user, signOut } = useAuth();

  if (!user) {
    return (
      <nav aria-label="Account">
        <a href="/login">Sign in</a>
      </nav>
    );
  }

  return (
    <section aria-label="Account menu">
      <h2>{user.name}</h2>
      <p>Role: {user.role}</p>
      <a href="/profile">View profile</a>
      {(user.role === 'editor' || user.role === 'admin') && (
        <a href="/articles/new">Create article</a>
      )}
      {user.role === 'admin' && (
        <a href="/admin/users">Manage users</a>
      )}
      <button type="button" onClick={signOut}>
        Sign out
      </button>
    </section>
  );
}

The branch conditions belong to this presentational example. In a real product, authorization must also be enforced on the server. Hiding an administration link improves the interface but does not secure the endpoint.

Accessible names make the behavior easy to understand and stable to locate. Avoid exporting user through data attributes solely for tests. The visible name, role, links, and button already express the useful contract.

Verify the step: run npm run build again. Render AccountMenu inside AuthProvider in the application and provide either user: null or a sample editor. Confirm the anonymous branch has one Sign in link and the editor branch has Create article but not Manage users.

Step 3: Mock React Context in Playwright Component Tests

Write a small value factory and mount the real provider around the subject. The callback can be a local function because the test only needs to observe whether clicking Sign out invokes the dependency.

// playwright/AccountMenu.spec.tsx
import { expect, test } from '@playwright/experimental-ct-react';
import { AccountMenu } from '../src/components/AccountMenu';
import {
  AuthProvider,
  type AuthContextValue,
} from '../src/context/AuthContext';

function authValue(
  overrides: Partial<AuthContextValue> = {},
): AuthContextValue {
  return {
    user: { id: 'user-1', name: 'Mina Patel', role: 'viewer' },
    signOut: () => {},
    ...overrides,
  };
}

test('shows sign in for an anonymous visitor', async ({ mount }) => {
  const component = await mount(
    <AuthProvider value={authValue({ user: null })}>
      <AccountMenu />
    </AuthProvider>,
  );

  await expect(
    component.getByRole('link', { name: 'Sign in' }),
  ).toHaveAttribute('href', '/login');
  await expect(
    component.getByRole('button', { name: 'Sign out' }),
  ).toHaveCount(0);
});

test('renders viewer identity and actions', async ({ mount }) => {
  const component = await mount(
    <AuthProvider value={authValue()}>
      <AccountMenu />
    </AuthProvider>,
  );

  await expect(
    component.getByRole('region', { name: 'Account menu' }),
  ).toContainText('Mina Patel');
  await expect(component).toContainText('Role: viewer');
  await expect(
    component.getByRole('link', { name: 'Create article' }),
  ).toHaveCount(0);
});

This is a context mock at the provider boundary. It is more faithful than mocking useAuth through a module system because React still performs provider lookup and renders the real consumer. The factory keeps irrelevant defaults short while each test makes the important difference visible.

Use factories carefully. If a default role changes from viewer to admin, tests can silently gain privileges. Keep the least privileged default and override roles explicitly in authorization cases.

Verify the step: run npm run test:ct -- playwright/AccountMenu.spec.tsx. Both cases should pass. Change the expected viewer text to admin once, confirm that Playwright reports the actual rendered role, then restore the assertion.

Step 4: Test Permission Branches and Context Callbacks

Add tests for editor and administrator values. Verify both presence and meaningful absence so a permission regression cannot expose a neighboring action. Then observe the callback through a local count.

// append to playwright/AccountMenu.spec.tsx
test('gives an editor authoring access but not admin access', async ({ mount }) => {
  const component = await mount(
    <AuthProvider value={authValue({
      user: { id: 'editor-1', name: 'Eli Chen', role: 'editor' },
    })}>
      <AccountMenu />
    </AuthProvider>,
  );

  await expect(
    component.getByRole('link', { name: 'Create article' }),
  ).toHaveAttribute('href', '/articles/new');
  await expect(
    component.getByRole('link', { name: 'Manage users' }),
  ).toHaveCount(0);
});

test('gives an administrator user management access', async ({ mount }) => {
  const component = await mount(
    <AuthProvider value={authValue({
      user: { id: 'admin-1', name: 'Ava Jones', role: 'admin' },
    })}>
      <AccountMenu />
    </AuthProvider>,
  );

  await expect(
    component.getByRole('link', { name: 'Create article' }),
  ).toBeVisible();
  await expect(
    component.getByRole('link', { name: 'Manage users' }),
  ).toHaveAttribute('href', '/admin/users');
});

test('calls signOut from context', async ({ mount }) => {
  let calls = 0;
  const component = await mount(
    <AuthProvider value={authValue({ signOut: () => { calls += 1; } })}>
      <AccountMenu />
    </AuthProvider>,
  );

  await component.getByRole('button', { name: 'Sign out' }).click();
  expect(calls).toBe(1);
});

Playwright CT can marshal many callback props used in mounted JSX, but keep callback checks narrow. The stronger assertion is normally the resulting UI. Here the account menu delegates sign-out rather than owning the signed-out state, so invocation is its boundary contract. A stateful provider test in Step 7 will cover the visible transition.

Do not add fixed waits after the click. Playwright awaits the browser action, and the plain counter is updated synchronously. For asynchronous behavior, assert an observable pending or completed state with a web-first assertion.

Verify the step: run the spec with --repeat-each=2. Five tests should pass on every repetition. Run the editor case alone by title and confirm it has no dependency on the viewer or administrator test.

Step 5: Compose Nested Providers with a Typed Harness

Production components often consume authentication, theme, locale, routing, or a data client together. Compose only the providers required by this component family. Start by adding a theme context and a banner that consumes both contexts.

// src/context/ThemeContext.tsx
import { createContext, type ReactNode, useContext } from 'react';

export type Theme = 'light' | 'dark';
const ThemeContext = createContext<Theme>('light');

export function ThemeProvider({
  theme,
  children,
}: { theme: Theme; children: ReactNode }) {
  return <ThemeContext.Provider value={theme}>{children}</ThemeContext.Provider>;
}

export function useTheme() {
  return useContext(ThemeContext);
}
// src/components/WelcomeBanner.tsx
import { useAuth } from '../context/AuthContext';
import { useTheme } from '../context/ThemeContext';

export function WelcomeBanner() {
  const { user } = useAuth();
  const theme = useTheme();
  return (
    <p role="status" data-theme={theme}>
      {user ? `Welcome, ${user.name}` : 'Welcome, guest'}
    </p>
  );
}

Place the reusable harness beside the component tests, not in production source:

// playwright/TestProviders.tsx
import type { ReactNode } from 'react';
import {
  AuthProvider,
  type AuthContextValue,
} from '../src/context/AuthContext';
import { ThemeProvider, type Theme } from '../src/context/ThemeContext';

export function TestProviders({
  auth,
  theme = 'light',
  children,
}: {
  auth: AuthContextValue;
  theme?: Theme;
  children: ReactNode;
}) {
  return (
    <ThemeProvider theme={theme}>
      <AuthProvider value={auth}>{children}</AuthProvider>
    </ThemeProvider>
  );
}

The harness requires auth because identity changes scenarios materially. Theme has a harmless documented default. Resist creating an AllProviders wrapper that boots analytics, a router, a query client, and a global store for every button test. Broad wrappers slow tests and obscure why the subject works.

Verify the step: run npm run build and confirm all source components compile. Playwright compiles files in its test directory when the relevant spec imports them, so the next step verifies the harness itself.

Step 6: Verify Nested Context Values and Provider Order

Mount WelcomeBanner through the harness with explicit authentication and theme values. Although checking data-theme is implementation-adjacent, it is the public styling hook in this small example. In a design system, a stable screenshot or computed style may express the theme contract better.

// playwright/WelcomeBanner.spec.tsx
import { expect, test } from '@playwright/experimental-ct-react';
import { WelcomeBanner } from '../src/components/WelcomeBanner';
import { TestProviders } from './TestProviders';

test('uses authentication and dark theme context', async ({ mount }) => {
  const component = await mount(
    <TestProviders
      theme="dark"
      auth={{
        user: { id: 'user-2', name: 'Noah Kim', role: 'editor' },
        signOut: () => {},
      }}
    >
      <WelcomeBanner />
    </TestProviders>,
  );

  const status = component.getByRole('status');
  await expect(status).toHaveText('Welcome, Noah Kim');
  await expect(status).toHaveAttribute('data-theme', 'dark');
});

test('uses the default light theme for a guest', async ({ mount }) => {
  const component = await mount(
    <TestProviders auth={{ user: null, signOut: () => {} }}>
      <WelcomeBanner />
    </TestProviders>,
  );

  const status = component.getByRole('status');
  await expect(status).toHaveText('Welcome, guest');
  await expect(status).toHaveAttribute('data-theme', 'light');
});

Provider order matters when an inner provider consumes an outer context during initialization. Document that dependency in the harness and test one representative integration. If providers do not depend on each other, choose a consistent order and avoid tests that merely duplicate React itself.

Choose the smallest pattern that fits the subject:

Pattern Best use Main risk
Provider inline in each test One context, few scenarios Repeated markup
Typed test harness Stable group of nested providers Hidden scenario defaults
Production app shell Shell behavior is the subject Slow, broad setup
Mocked custom hook module Rare module-boundary isolation Skips real provider wiring

For the underlying mount API and JSX callback rules, follow mounting React components in Playwright step by step.

Verify the step: run npm run test:ct -- playwright/WelcomeBanner.spec.tsx. Both cases should pass. Swap the provider order in TestProviders; these independent providers should still work, proving the test does not assert an accidental nesting detail. Restore the documented order afterward.

Step 7: Test Context Updates Through User Behavior

Static values cover most permission branches, but consumers must sometimes respond when a provider changes. Build a stateful host in test support code. Its button changes the provider value, allowing the browser test to exercise a real React update without reaching into context internals.

// playwright/StatefulAuthHost.tsx
import { useState, type ReactNode } from 'react';
import {
  AuthProvider,
  type AuthUser,
} from '../src/context/AuthContext';

export function StatefulAuthHost({ children }: { children: ReactNode }) {
  const [user, setUser] = useState<AuthUser | null>({
    id: 'editor-2',
    name: 'Sam Rivera',
    role: 'editor',
  });

  return (
    <AuthProvider value={{ user, signOut: () => setUser(null) }}>
      {children}
    </AuthProvider>
  );
}
// playwright/AccountMenuTransition.spec.tsx
import { expect, test } from '@playwright/experimental-ct-react';
import { AccountMenu } from '../src/components/AccountMenu';
import { StatefulAuthHost } from './StatefulAuthHost';

test('renders the anonymous branch after sign out', async ({ mount }) => {
  const component = await mount(
    <StatefulAuthHost>
      <AccountMenu />
    </StatefulAuthHost>,
  );

  await expect(component).toContainText('Sam Rivera');
  await component.getByRole('button', { name: 'Sign out' }).click();
  await expect(
    component.getByRole('link', { name: 'Sign in' }),
  ).toBeVisible();
  await expect(component).not.toContainText('Sam Rivera');
});

The interaction proves that the callback changes provider state and that the consumer rerenders into the anonymous branch. This offers more confidence than checking only that a callback ran. Keep the host specific to the behavior under test so it remains easy to read.

React context compares provider values by identity. Memoization can prevent unnecessary rerenders in production, but do not add useMemo to the test harness unless the tested behavior depends on referential stability. Tests should not optimize away a defect or recreate implementation machinery without a reason.

Verify the step: run npm run test:ct -- playwright/AccountMenuTransition.spec.tsx --repeat-each=3. Every run should begin as Sam Rivera and end at Sign in. If a later repetition starts anonymous, state is leaking outside the mounted host.

Step 8: Prove Isolation and Missing-Provider Behavior

Each mount creates a new React tree, but module variables, singleton stores, cached clients, and mutable factory objects can still leak. Construct new objects inside each test and never mutate a shared default. Run permission cases in parallel and repeated mode to expose accidental coupling.

A strict custom hook also deserves one focused contract test. Since the render intentionally throws, capture only the known page error and assert its message. Do not suppress every browser exception globally.

// playwright/AuthProviderContract.spec.tsx
import { expect, test } from '@playwright/experimental-ct-react';
import { AccountMenu } from '../src/components/AccountMenu';

test('explains when AuthProvider is missing', async ({ mount, page }) => {
  const errorPromise = page.waitForEvent('pageerror');

  await expect(mount(<AccountMenu />)).rejects.toThrow();
  const error = await errorPromise;
  expect(error.message).toContain(
    'useAuth must be used inside AuthProvider',
  );
});

Depending on the locked React and CT versions, a render failure can surface through the rejected mount, the page error, or both. The useful contract is the diagnostic message. If your runner does not reject mount, remove that rejection assertion and await the captured page error with a short test timeout. Keep this one compatibility-sensitive case isolated from user-behavior specs.

Use an immutable factory return for every scenario. Avoid code such as const defaultAuth = {...} followed by defaultAuth.user.role = 'admin'. That mutation survives into later tests in the worker. Returning a new nested user object each time makes ownership clear.

Verify the step: run npm run test:ct -- --fully-parallel --repeat-each=2. All behavior tests must pass regardless of order. The provider-contract case must report the expected message without hanging. Also run npm run test:ct -- --list to confirm every intended spec is discovered.

Mock React Context Playwright Component Tests: Design Rules

Treat context as an environmental input, not a target for deep implementation assertions. Your component test should state who the user is, what permissions or preferences apply, what action occurs, and what the browser shows. A provider test can separately verify state transitions that the provider itself owns.

Use the real provider when it is a thin context boundary. If the production provider performs authentication requests, refresh timers, storage reads, and redirects, split those concerns behind injectable services or mount a smaller deterministic provider. Starting the entire identity platform defeats the speed and isolation expected from component testing.

Keep authorization scenarios explicit. Viewer, editor, and administrator cases should construct their own values. Prefer semantic builders such as editorUser() only when they improve clarity and always return fresh objects. Never use production tokens or copied personal data in a component fixture.

Assert accessible outcomes. Role and label locators tolerate changes to markup and styling while encouraging usable components. A context test that asserts a CSS class or hook call often tests the implementation rather than the user consequence. Theme is an exception only when styling itself is the contract, in which case a bounded visual assertion can be appropriate.

Finally, preserve coverage at other layers. A context mock does not prove that the backend returns the right role, the login cookie is accepted, or a protected endpoint rejects a viewer. Cover those risks with API contract, integration, and a small number of end-to-end tests.

Troubleshooting

useAuth must be used inside AuthProvider appears in a normal test -> Wrap the subject at mount time or use the typed TestProviders harness. Check whether a portal or separately mounted child sits outside the provider tree.

A role-specific link is visible in the wrong test -> Return a fresh nested user from the factory and stop mutating shared defaults. Run with --fully-parallel --repeat-each=3 to reproduce leakage.

The component never reflects a changed context value -> Put the value in React state and pass the new object to the provider. Mutating the existing object does not create a provider value identity change.

The callback cannot be serialized or behaves unexpectedly -> Keep callback props declared directly in the JSX passed to mount, use supported CT callback marshalling, and assert browser-visible results when possible. Avoid closures over complex nonserializable objects.

A custom alias or provider import fails -> Confirm the spec uses .tsx, the CT config imports the React package, and Vite resolves the same aliases and plugins as the component build. Start with relative imports to separate alias problems from provider problems.

The intentional missing-provider case produces noisy errors -> Capture the exact expected pageerror inside that test. Do not install a global listener that hides unrelated browser errors. Adjust the rejection assertion to the behavior of your locked React and Playwright versions.

Where To Go Next

Return to the complete Playwright Component Testing for React guide to place context scenarios in a balanced test strategy. If your next component needs a more advanced mount lifecycle, use the step-by-step React component mounting tutorial.

Resilience is the next useful branch. Learn to test React error boundaries with Playwright when providers or descendants fail during rendering. Then use the Playwright component tests with Vite and CI tutorial to run the same isolated suite on pull requests and retain traces for failures.

For your production suite, add one context scenario at a time. Start with the least privileged user, add the highest-risk allowed role, cover the denied action, and then add a state transition only if the component owns one.

Interview Questions and Answers

Q: How do you mock React context in a Playwright component test?

Mount the component inside the real context provider and pass a deterministic value for the scenario. This preserves React provider lookup and consumer rendering while removing external authentication or application bootstrap dependencies.

Q: Why is a real provider usually better than mocking the custom hook?

The real provider exercises production composition and catches a missing or changed provider contract. A module-level hook mock can be useful for rare isolation needs, but it bypasses the React context boundary and can create false confidence.

Q: When should you create a reusable provider harness?

Create one when a stable group of providers is repeated across several component specs. Keep risk-defining inputs such as user, permissions, locale, and route explicit, and avoid booting unrelated global services.

Q: How do you prevent context state from leaking between tests?

Create a new value, nested objects, provider, and stateful client for each mount. Avoid mutable module defaults, then run the suite fully parallel and repeated to reveal coupling.

Q: How do you test a context value update?

Mount the consumer inside a test host that owns provider value state. Trigger the update through a user action and use web-first assertions to verify the resulting visible UI.

Q: Does mocking permission context test application security?

No. It verifies conditional presentation for controlled inputs. Server authorization, token validation, and protected endpoints need API, integration, and end-to-end security coverage.

Best Practices and Common Mistakes

  • Use the production provider interface instead of reaching into the raw context object.
  • Give the default factory the least privilege and override important values explicitly.
  • Return fresh nested objects from every factory call.
  • Keep provider harnesses small, typed, and beside test support code.
  • Assert visible actions, messages, and accessible semantics.
  • Test both allowed and denied branches for high-risk permissions.
  • Prefer a visible state transition over a callback-count assertion when the provider owns state.
  • Do not use a component context mock as proof of backend authorization.
  • Do not hide user identity or role in a global test setup.
  • Do not silence all page errors to accommodate one intentional failure case.

Mock React Context Playwright Component Tests: Conclusion

The dependable way to mock react context playwright component tests is to control the value at the real provider boundary. Start inline, introduce a typed harness only when provider composition repeats, and use a stateful host when the behavior depends on context updates.

Keep each identity and permission scenario explicit, create fresh state for every mount, and assert what the user can see or do. Run the suite in parallel and repeated mode, then retain API and end-to-end tests for the real authentication and authorization boundaries that a context mock intentionally excludes.

Interview Questions and Answers

How would you mock React context in a Playwright component test?

I would mount the subject inside its real provider and supply a typed, deterministic value for the scenario. This preserves provider lookup and consumer rendering without booting external identity or global application state. I would assert user-visible behavior through roles and accessible names.

Why prefer a provider wrapper over mocking a context hook?

A provider wrapper exercises the production context contract and component composition. Mocking the hook bypasses that wiring and can hide a missing provider or incompatible value shape. I reserve hook mocks for deliberate module isolation where that tradeoff is understood.

What makes a good reusable test provider harness?

It is small, typed, deterministic, and composes only providers required by the tested component family. Important scenario inputs stay explicit and defaults are low-risk. It creates fresh state per mount and avoids starting the complete app shell.

How would you test role-based UI with React context?

I would create separate viewer, editor, and administrator values and mount each through the provider. I would assert both allowed actions and important denied actions. I would also explain that these tests cover presentation, while server authorization needs separate coverage.

How do you test context updates rather than static values?

I use a stateful test host that owns the provider value. A browser action changes the host state, and web-first assertions verify that the real consumer rerenders to the expected UI. This avoids manipulating React internals.

How do you keep provider-based component tests isolated?

I return fresh values and nested objects for each mount, avoid mutable module defaults, and construct clients or stores per test. Then I run files fully parallel and repeated to expose order dependence. Any global cleanup is a safety net, not the primary isolation mechanism.

Frequently Asked Questions

Can Playwright component tests use React context providers?

Yes. Pass JSX containing the real provider and the component to the `mount` fixture. Supply a deterministic provider value for each scenario and assert the rendered browser behavior.

Should I mock useContext directly in Playwright?

Usually no. Mounting with the real provider preserves React composition and tests the consumer more faithfully. Mock a custom hook only when you deliberately want a narrower module boundary and accept that provider wiring is excluded.

How do I test multiple React contexts together?

Create a small typed test harness that nests the required production providers. Require scenario-defining values as props, provide only harmless defaults, and keep unrelated application services out of the wrapper.

How do I change a context value during a component test?

Mount the consumer inside a test host that stores the provider value in React state. Trigger a user action that updates that state, then use Playwright web-first assertions to verify the new visible branch.

Why do React context component tests pass alone but fail together?

A shared mutable object, singleton store, cached client, or module variable is probably leaking between mounts. Construct fresh nested values for every test and run fully parallel with repeated execution to find the coupling.

Does a mocked auth context verify authorization security?

No. It verifies how UI behaves for a supplied identity or role. Verify real authorization with backend API and integration tests, plus selected end-to-end journeys through actual authentication.

Related Guides