---
title: "Testing Strategies"
description: "Write tests that validate routing configuration and `proxy.ts` matchers before deployment."
canonical_url: "https://vercel.com/academy/microfrontends-on-vercel/testing-strategies"
md_url: "https://vercel.com/academy/microfrontends-on-vercel/testing-strategies.md"
docset_id: "vercel-academy"
doc_version: "1.0"
last_updated: "2026-11-04T22:26:55.305Z"
content_type: "lesson"
course: "microfrontends-on-vercel"
course_title: "Microfrontends on Vercel"
prerequisites:  []
---

<agent-instructions>
Vercel Academy — structured learning, not reference docs.
Lessons are sequenced.
Adapt commands to the human's actual environment (OS, package manager, shell, editor) — detect from project context or ask, don't assume.
The lesson shows one path; if the human's project diverges, adapt concepts to their setup.
Preserve the learning goal over literal steps.
Quizzes are pedagogical — engage, don't spoil.
Quiz answers are included for your reference.
</agent-instructions>

# Testing Strategies

# Testing Strategies

Routing misconfigurations are the most common microfrontends problem. A wrong path pattern can send users to 404 pages. Tests catch these before deployment.

## Outcome

Write routing validation tests using `@vercel/microfrontends/next/testing` utilities.

## The Problem: Configuration Drift

As your application grows, routing changes:

- New paths added to apps
- `proxy.ts` matchers updated
- Redirects added or removed

Without tests, these changes can break routing silently. A redirect in marketing that conflicts with a docs path? You won't know until users complain.

## Testing Utilities

The `@vercel/microfrontends` package includes three test utilities:

| Utility                            | Purpose                                      |
| ---------------------------------- | -------------------------------------------- |
| `validateRouting`                  | Verify paths route to correct apps           |
| `validateMiddlewareConfig`         | Ensure `proxy.ts` matchers work with routing |
| `validateMiddlewareOnFlaggedPaths` | Test feature flag routing behavior           |

## Fast Track

1. Install Jest in the marketing app
2. Write routing validation tests
3. Add a `proxy.ts` and test its matcher
4. Run tests in CI

## Hands-on Exercise 3.2

Add routing tests to the Acme Platform.

### Part 1: Set Up Jest

Add Jest to the marketing app (where `microfrontends.json` lives):

```bash
cd apps/marketing
pnpm add -D jest @types/jest ts-jest
```

Create `apps/marketing/jest.config.js`:

```javascript title="apps/marketing/jest.config.js"
/** @type {import('jest').Config} */
const config = {
  testEnvironment: "node",
  transform: {
    "^.+\\.tsx?$": ["ts-jest", { useESM: true }],
  },
  moduleNameMapper: {
    "^@/(.*)$": "<rootDir>/$1",
  },
  testMatch: ["**/__tests__/**/*.test.ts"],
};

module.exports = config;
```

Add test script to `apps/marketing/package.json`:

```json title="apps/marketing/package.json"
{
  "scripts": {
    "dev": "next dev --port 3000",
    "build": "next build",
    "start": "next start",
    "lint": "eslint",
    "test": "jest"
  }
}
```

### Part 2: Write Routing Tests

Create `apps/marketing/__tests__/routing.test.ts`:

```typescript title="apps/marketing/__tests__/routing.test.ts"
/* @jest-environment node */
import { validateRouting } from "@vercel/microfrontends/next/testing";

describe("microfrontends routing", () => {
  const configPath = "./microfrontends.json";

  test("routes marketing paths to marketing app", () => {
    expect(() =>
      validateRouting(configPath, {
        "acme-marketing": ["/", "/pricing", "/about", "/contact"],
      })
    ).not.toThrow();
  });

  test("routes docs paths to docs app", () => {
    expect(() =>
      validateRouting(configPath, {
        "acme-docs": [
          "/docs",
          "/docs/getting-started",
          "/docs/api",
          "/docs/api/reference",
          "/docs/guides/setup",
        ],
      })
    ).not.toThrow();
  });

  test("routes dashboard paths to dashboard app", () => {
    expect(() =>
      validateRouting(configPath, {
        "acme-dashboard": [
          "/app",
          "/app/projects",
          "/app/projects/123",
          "/settings",
          "/settings/profile",
          "/settings/billing",
        ],
      })
    ).not.toThrow();
  });

  test("404 paths fall through to default app", () => {
    expect(() =>
      validateRouting(configPath, {
        "acme-marketing": ["/unknown-page", "/random/nested/path"],
      })
    ).not.toThrow();
  });
});
```

The keys are the application keys from `microfrontends.json`. A typo like `"marketing"` fails with `Application marketing does not exist in the microfrontends config`, which is a nice way to find out.

### Part 3: Add a Proxy and Test Its Matcher

The marketing app doesn't have a `proxy.ts` yet. Give it one that runs the microfrontends middleware. With no flags it leaves routing alone for now, and Lesson 4.1 adds a flag to it.

```typescript title="apps/marketing/proxy.ts"
import type { NextRequest } from "next/server";
import { runMicrofrontendsMiddleware } from "@vercel/microfrontends/next/middleware";

export async function proxy(request: NextRequest) {
  const response = await runMicrofrontendsMiddleware({
    request,
    flagValues: {},
  });

  if (response) {
    return response;
  }
}

export const config = {
  matcher: ["/.well-known/vercel/microfrontends/client-config"],
};
```

The package still says "middleware" in its function names. They work in Next.js 16's `proxy.ts`. The matcher includes `/.well-known/vercel/microfrontends/client-config` because the browser asks that endpoint which app serves a flagged path.

Create `apps/marketing/__tests__/proxy.test.ts`:

```typescript title="apps/marketing/__tests__/proxy.test.ts"
/* @jest-environment node */
import { validateMiddlewareConfig } from "@vercel/microfrontends/next/testing";
import { config as proxyConfig } from "../proxy";

describe("proxy configuration", () => {
  const configPath = "./microfrontends.json";

  test("marketing proxy config is compatible with microfrontends", () => {
    expect(() =>
      validateMiddlewareConfig(proxyConfig, configPath)
    ).not.toThrow();
  });
});
```

This test fails if the matcher catches a path that belongs to a child app, like `/docs`. Those requests never reach the default app's proxy, so a matcher that claims them is lying to you. It also fails if the client-config path is missing.

### Part 4: Run the Tests

```bash
cd apps/marketing
pnpm test
```

Expected output:

```
PASS  __tests__/routing.test.ts
  microfrontends routing
    ✓ routes marketing paths to marketing app (15 ms)
    ✓ routes docs paths to docs app (3 ms)
    ✓ routes dashboard paths to dashboard app (2 ms)
    ✓ 404 paths fall through to default app (2 ms)

PASS  __tests__/proxy.test.ts
  proxy configuration
    ✓ marketing proxy config is compatible with microfrontends (8 ms)

Test Suites: 2 passed, 2 total
Tests:       5 passed, 5 total
```

## Catching Misconfigurations

What happens when routing is wrong?

Add a failing test to see the error:

```typescript
test("EXAMPLE: this would catch a misconfiguration", () => {
  expect(() =>
    validateRouting("./microfrontends.json", {
      // Wrong! /docs should route to acme-docs, not acme-marketing
      "acme-marketing": ["/docs"],
    })
  ).toThrow();
});
```

Flip `.toThrow()` to `.not.toThrow()` and Jest prints exactly what's wrong:

```
Incorrect microfrontends routing detected:

- Expected /docs to match acme-marketing, but it matched acme-docs (on /docs, /docs/:path*).
```

## Adding Tests to CI

Update `turbo.json` to include tests:

```json title="turbo.json" {7-9}
{
  "$schema": "https://turbo.build/schema.json",
  "tasks": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": [".next/**", "!.next/cache/**"]
    },
    "test": {
      "dependsOn": ["^build"]
    },
    "dev": {
      "cache": false,
      "persistent": true
    },
    "lint": {
      "dependsOn": ["^lint"]
    }
  }
}
```

Add root-level test script to `package.json`:

```json title="package.json"
{
  "scripts": {
    "dev": "turbo dev",
    "build": "turbo build",
    "test": "turbo test",
    "lint": "turbo lint"
  }
}
```

Now `pnpm test` runs tests across all apps.

## Commit

```bash
git add -A
git commit -m "test: add microfrontends routing validation tests"
```

## Done-When

- [ ] Jest configured in marketing app
- [ ] Routing tests pass for all three apps
- [ ] `proxy.ts` configuration test passes
- [ ] Tests can run via `pnpm test` from root

## Test Coverage Strategy

| What to Test        | Why                           |
| ------------------- | ----------------------------- |
| All known paths     | Ensure routing doesn't break  |
| Nested paths        | Verify wildcard patterns work |
| Edge cases (404s)   | Confirm fallback behavior     |
| `proxy.ts` matchers | Prevent matcher conflicts     |
| Feature flag paths  | Validate conditional routing  |

## What's Next

Debug headers and tracing for observing routing decisions in production.


---

[Full course index](/academy/llms.txt) · [Sitemap](/academy/sitemap.md)
