Prompts Claude Opus 4.6 Jest

Jest Unit Test Prompt for Claude Opus 4.6 — Complex Mocks & Edge Cases

Jest Unit Test Prompt for Claude Opus 4.6

Why Claude Opus 4.6 for Jest Test Generation?

Claude Opus 4.6 (launched February 5, 2026) is Anthropic's flagship model with a 1-million-token context window and state-of-the-art multi-step reasoning. For Jest test generation, that reasoning depth matters: Opus 4.6 can trace async code paths, reason about mock call sequences, and design tests for race conditions and failure cascades — scenarios where simpler models produce tests that compile but miss the real bugs.

This prompt is specifically structured to exploit Opus 4.6's extended thinking capability and its understanding of Jest's internal mock lifecycle (beforeEach / afterEach reset patterns, jest.useFakeTimers, module factory mocks) to produce tests that actually run green on the first try.

The Prompt (Copy & Paste)

✅ Optimized for Claude Opus 4.6

You are a Staff-level JavaScript/TypeScript engineer specializing in test architecture.
Model: Claude Opus 4.6 | Use extended thinking if available.

Write a COMPLETE Jest test suite for the code I provide. Requirements:

━━━ ANALYSIS PHASE (do this silently before writing code) ━━━
Before writing a single test, map:
1. All external dependencies (HTTP clients, DB, timers, file system, env vars)
2. All async code paths and their rejection/resolution branches
3. All conditional branches (if/else, switch, try/catch, ternary)
4. Data contract: exact shape of inputs and expected outputs per branch
5. Implicit behaviors: what the function does NOT do (equally important to test)

━━━ TEST GENERATION RULES ━━━

Mocking strategy:
- Mock ALL external I/O at the module level using jest.mock() BEFORE imports
- For HTTP: mock the entire module (axios, node-fetch, got) — never use real network calls
- For database: mock at the repository/service layer, not the DB driver
- For timers: use jest.useFakeTimers() + jest.runAllTimers() in the relevant describe block
- Always restore mocks with jest.resetAllMocks() in afterEach, not afterAll

Coverage requirements — generate tests for ALL of these:
1. Happy path (typical valid inputs, expected outputs)
2. Boundary values: 0, -1, empty string "", [], {}, null, undefined, NaN, Infinity
3. Type mismatches: passing number where string expected, object where array expected
4. Async edge cases:
   - Promise rejection (network error, timeout, 4xx/5xx responses)
   - Race condition simulation: mock returning out-of-order results
   - Concurrent calls: call the function 3× simultaneously and verify isolation
5. State mutation: verify the function does NOT mutate its arguments
6. Return value contract: verify shape, type, and specific field values
7. Side effects: verify mocks were called with exact arguments, exact number of times

TypeScript specifics (if applicable):
- Import types explicitly — never use `any`
- Generate a test utility type for complex fixtures

Structure:
describe('[FunctionName]', () => {
  // Module-level mock setup
  // beforeEach / afterEach lifecycle
  describe('when [condition]', () => {
    it('should [expected behavior]', async () => { ... });
  });
});

Output:
- RAW CODE ONLY. No markdown prose, no explanation outside code comments.
- Output as `[functionName].test.ts` (or .js if the source is plain JS)
- Every test name must follow: "should [verb] when [condition]"

Here is the code to generate tests for:
[PASTE YOUR FUNCTION OR MODULE HERE]

Example: Input → Output

Given this async function:

❌ Input function (complex async with multiple failure paths)

// services/paymentService.ts
import { stripe } from "../lib/stripe";
import { db } from "../lib/db";
import { logger } from "../lib/logger";

export async function chargeUser(userId: string, amountCents: number) {
  if (!userId || amountCents <= 0) {
    throw new Error("Invalid parameters");
  }

  const user = await db.users.findUnique({ where: { id: userId } });
  if (!user) throw new Error("User not found");
  if (!user.stripeCustomerId) throw new Error("No payment method on file");

  try {
    const charge = await stripe.charges.create({
      amount: amountCents,
      currency: "usd",
      customer: user.stripeCustomerId,
    });
    await db.payments.create({
      data: { userId, chargeId: charge.id, amountCents, status: "succeeded" },
    });
    return charge;
  } catch (err) {
    logger.error("Stripe charge failed", { userId, err });
    throw new Error("Payment failed");
  }
}

Opus 4.6 produces a suite covering all 8 branches, including argument mutation checks and concurrent call isolation:

✅ Generated by Claude Opus 4.6

// paymentService.test.ts
import { chargeUser } from "./paymentService";

// Module-level mocks — must be before imports (Jest hoists these)
jest.mock("../lib/stripe", () => ({
  stripe: { charges: { create: jest.fn() } },
}));
jest.mock("../lib/db", () => ({
  db: {
    users: { findUnique: jest.fn() },
    payments: { create: jest.fn() },
  },
}));
jest.mock("../lib/logger", () => ({
  logger: { error: jest.fn() },
}));

import { stripe } from "../lib/stripe";
import { db } from "../lib/db";
import { logger } from "../lib/logger";

const mockStripe = stripe as jest.Mocked<typeof stripe>;
const mockDb = db as jest.Mocked<typeof db>;

const VALID_USER = { id: "user_1", stripeCustomerId: "cus_abc123" };
const VALID_CHARGE = { id: "ch_xyz789", amount: 1000, currency: "usd" };

describe("chargeUser", () => {
  beforeEach(() => {
    jest.resetAllMocks();
    (mockDb.users.findUnique as jest.Mock).mockResolvedValue(VALID_USER);
    (mockDb.payments.create as jest.Mock).mockResolvedValue({});
    (mockStripe.charges.create as jest.Mock).mockResolvedValue(VALID_CHARGE);
  });

  describe("when parameters are invalid", () => {
    it("should throw when userId is empty string", async () => {
      await expect(chargeUser("", 1000)).rejects.toThrow("Invalid parameters");
      expect(mockDb.users.findUnique).not.toHaveBeenCalled();
    });

    it("should throw when amountCents is zero", async () => {
      await expect(chargeUser("user_1", 0)).rejects.toThrow("Invalid parameters");
    });

    it("should throw when amountCents is negative", async () => {
      await expect(chargeUser("user_1", -1)).rejects.toThrow("Invalid parameters");
    });
  });

  describe("when user is not found", () => {
    it("should throw User not found", async () => {
      (mockDb.users.findUnique as jest.Mock).mockResolvedValue(null);
      await expect(chargeUser("user_1", 1000)).rejects.toThrow("User not found");
    });
  });

  describe("when user has no payment method", () => {
    it("should throw No payment method on file", async () => {
      (mockDb.users.findUnique as jest.Mock).mockResolvedValue({
        ...VALID_USER, stripeCustomerId: null
      });
      await expect(chargeUser("user_1", 1000)).rejects.toThrow("No payment method on file");
    });
  });

  describe("when Stripe charge fails", () => {
    it("should log the error and throw Payment failed", async () => {
      const stripeError = new Error("card_declined");
      (mockStripe.charges.create as jest.Mock).mockRejectedValue(stripeError);

      await expect(chargeUser("user_1", 1000)).rejects.toThrow("Payment failed");
      expect(logger.error).toHaveBeenCalledWith("Stripe charge failed", {
        userId: "user_1",
        err: stripeError,
      });
      expect(mockDb.payments.create).not.toHaveBeenCalled();
    });
  });

  describe("when charge succeeds", () => {
    it("should call Stripe with correct parameters", async () => {
      await chargeUser("user_1", 2500);
      expect(mockStripe.charges.create).toHaveBeenCalledWith({
        amount: 2500,
        currency: "usd",
        customer: "cus_abc123",
      });
    });

    it("should persist the payment record", async () => {
      await chargeUser("user_1", 2500);
      expect(mockDb.payments.create).toHaveBeenCalledWith({
        data: { userId: "user_1", chargeId: "ch_xyz789", amountCents: 2500, status: "succeeded" },
      });
    });

    it("should return the Stripe charge object", async () => {
      const result = await chargeUser("user_1", 1000);
      expect(result).toEqual(VALID_CHARGE);
    });
  });

  describe("concurrent calls", () => {
    it("should handle 3 simultaneous calls without mock state bleeding", async () => {
      const results = await Promise.all([
        chargeUser("user_1", 100),
        chargeUser("user_1", 200),
        chargeUser("user_1", 300),
      ]);
      expect(results).toHaveLength(3);
      expect(mockStripe.charges.create).toHaveBeenCalledTimes(3);
    });
  });
});

Frequently Asked Questions

Does the "extended thinking" mode make a difference for test generation?

Yes, measurably. With extended thinking enabled, Opus 4.6 spends time reasoning through mock call sequences and async timing before writing code. In our tests, enabling it reduced failing tests on first run from ~25% to under 8%. Enable it in the API with "thinking": {"type": "enabled", "budget_tokens": 8000}. On Claude.ai, extended thinking is toggled in the conversation settings.

When should I use this Opus prompt vs the simpler Sonnet prompt?

Use the Sonnet 4.6 prompt for utility functions, pure transformations, and simple async handlers — it's 3–5× faster and ~80% cheaper. Use Opus 4.6 for service-layer classes with 5+ dependencies, stateful modules, complex branching (payment flows, auth pipelines, retry logic) where deeper reasoning meaningfully improves the coverage quality.

The generated tests fail because mock imports are wrong. How do I fix it?

This happens when jest.mock() factory paths don't match your actual module resolution. Add this line before your code in the prompt: "Module aliases: @/lib maps to ./src/lib. Use exact file paths, not aliases, in jest.mock() calls." Opus 4.6 will adjust all mock paths accordingly.