GPTPROMPTS.AI
HOW-TO GUIDE · 2026

How to Use GitHub Copilot for Tests: 2026 Guide

An 8-step workflow for working engineers. /tests slash command tuned to your repo conventions, edge-case enumeration, fixture factories, integration boundaries, coverage-gap analysis, property-based testing, and the spec-not-implementation review discipline that keeps tests from becoming tautologies.

Writing tests is the AI use case where the productivity gain is most consistent and the quality risk is most subtle. Copilot can scaffold a test file in 30 seconds that would take a human 20 minutes. The risk is that those generated tests can pass while asserting nothing meaningful, a class of bug called the tautological test, where the assertions mirror the current implementation and fail to catch regressions when the implementation is later wrong. The difference between a productive Copilot test workflow and an actively counterproductive one is the discipline of spec-driven generation, structured edge-case enumeration, and the deliberate-wrong-implementation review.

The 8-step workflow below is built for working engineers: backend developers retrofitting tests onto untested services, frontend developers writing component tests with React Testing Library, full-stack engineers writing integration tests at API and database boundaries, SREs writing post-incident regression tests, platform engineers building the shared test infrastructure that every team builds on top of. Steps 1 and 2 cover the convention setup and the precise /tests invocation that determine 60% of generation quality. Steps 3 through 7 cover the specific test types: edge case enumeration, fixture factories, integration tests, coverage gaps, property-based tests. Step 8 is the review discipline that keeps tests asserting the spec rather than the implementation.

Who this guide is for

  • • Backend engineers writing unit and integration tests for services in Node.js, Python, Java, C#, Go, Ruby, or Rust
  • • Frontend engineers writing component tests with React Testing Library, Vue Test Utils, or Angular TestBed, and E2E tests with Playwright or Cypress
  • • Full-stack engineers who own tests at the API boundary and across the frontend-backend integration
  • • QA and test engineers retrofitting test suites onto legacy code, building integration and contract test infrastructure, or owning the E2E test architecture
  • • SREs and on-call engineers writing post-incident regression tests that prevent the same production bug from reappearing
  • • Platform and DevOps engineers building shared test fixtures, factory libraries, and CI-integrated coverage tooling that every team builds on
  • • TDD practitioners using Copilot to generate the failing-test scaffolding before implementation
  • • Engineering leads and tech leads setting team-wide test conventions and reviewing AI-generated tests in code review

Why GitHub Copilot specifically (vs. Claude, Cursor, or ChatGPT)

For in-IDE test generation, GitHub Copilot has four structural advantages over alternatives in 2026. First, the purpose-built /tests slash command is tuned for test scaffolding in a way freeform prompts in other tools are not. /tests reads the project's existing test conventions (Jest describe/it blocks, pytest fixture patterns, JUnit annotations, Go testing.T idioms, RSpec contexts, xUnit test classes) and produces tests that match the project style without you specifying it. Where ChatGPT or Claude gives you generic Jest boilerplate, /tests in Copilot gives you tests that match the exact style of your existing test suite. Second, workspace indexing means Copilot sees the function under test plus its callers, the existing test fixtures, the test-helper utilities, and the mock factories already in your repo. The generated test reuses your existing helpers rather than inventing parallel ones, which is the single biggest quality difference between Copilot-generated tests and generic AI-generated tests. Third, deepest IDE integration across VS Code, Visual Studio, JetBrains IDEs (IntelliJ, PyCharm, WebStorm, GoLand, Rider, Android Studio), Neovim, Xcode, and Eclipse, with consistent /tests behavior across all of them. Fourth, native GitHub integration: Copilot reads the linked issue or PR description and can generate tests that match the acceptance criteria, which other tools cannot do.

Where Copilot loses on testing specifically: Claude is stronger when you need to reason about complex test architecture (test pyramid design, contract testing strategy, end-to-end test stability) because the longer context window handles a wider system view. ChatGPT with the reasoning models is better for property-based test design where the model needs to enumerate invariants thoughtfully. Cursor's multi-file agent is better for retrofitting tests across an entire untested module in one pass. Most working engineers use Copilot as the daily driver for per-function and per-class test generation, and reach for Claude on the harder test-strategy questions or for the largest cross-module retrofits.

The 8 steps below are tuned specifically for Copilot. The underlying discipline (write the spec before the test, generate tests from the spec not from the implementation, enumerate edge cases as a separate step, verify tests assert the spec) is tool-agnostic; the specific tactics (/tests with structured specs, workspace-indexed fixture reuse, the two-step enumeration workflow, the deliberate-wrong-implementation review) are Copilot-specific in 2026. For related Copilot workflows, see our Copilot for debugging guide, the Copilot prompt generator for reusable test prompts, and the best AI coding tools roundup for the broader landscape.

The 8-Step Workflow

01

Establish reference tests and test conventions before generating at scale

Copilot test generation is materially better when it has examples to follow. Before generating tests for an untested module, write 1 to 3 reference tests by hand that establish the conventions: where tests live in the file tree (alongside source, in __tests__/, in parallel test/ directory), how fixtures are built (inline objects, factory functions, JSON files), how mocks are organized (in __mocks__/ adjacent to source, in test/mocks/, inline), what the assertion style is (BDD describe/it, flat test() blocks, table-driven tests), and how test names are written ('returns 401 when credentials are invalid' vs 'should return 401 for invalid credentials'). Once the references exist, Copilot reads them as the workspace pattern and matches the style on every subsequent /tests invocation. The 30 minutes spent on reference tests saves hours of inconsistent output later. For a brand-new project, establish references at the start of test-writing. For an existing project with test conventions, the workspace already has the references; verify them by running /tests on a simple function and checking that the output matches the existing style. If it does not match, open one existing test file in the editor while running /tests so the convention is in the active context.

Example prompt
Before generating tests at scale, write the reference test. Then to extend: 'Here is the reference test for [first function]: [paste]. Generate tests for [second function] following the exact same patterns: same file location, same fixture style, same mock approach, same naming convention, same describe/it nesting structure. The second function does [behavior]; cover the happy path and 4 specific edge cases I will list: [list].'
02

Use /tests with a precise spec, not just the function name

The /tests slash command with no specification produces generic test scaffolding; with a precise specification it produces targeted tests covering the behaviors you actually care about. The pattern that works: select the function in the editor, open Copilot Chat (Cmd+I or Ctrl+I in VS Code), type /tests followed by the specification. The specification has 3 elements. First, the function purpose in one sentence ('createOrder takes a customer and a cart and creates a new order, deducting inventory and publishing an order-created event'). Second, the behaviors to cover, each named explicitly ('happy path with 2 items, empty cart returns 400, unauthorized returns 401, inventory not available returns 409 with the specific product IDs, idempotency key replay returns the original order, payment decline rolls back inventory'). Third, the fixtures and helpers to use ('use the existing customerFactory, orderFactory, and inventoryFactory in test/factories.ts; use the test-server setup in test/setup-server.ts'). The 3-element spec gives /tests the targeting it needs to produce tests that match your intent rather than generic boilerplate. Without the spec, you spend more time editing the generated tests than you would have spent writing them by hand.

Example prompt
'/tests for the [function name] I have selected. Function purpose: [one sentence on what it does]. Behaviors to cover: (1) [behavior with expected outcome], (2) [behavior with expected outcome], (3) [behavior with expected outcome], (4) [edge case], (5) [error case], (6) [boundary case]. Fixtures: use [factory name] in [location]; use [helper] in [location]. Test framework: [Jest/pytest/JUnit/etc.] matching the existing conventions in this repo. Each test should have a clear name describing the behavior being asserted.'
03

Run the two-step edge case enumeration to find what /tests missed

The default /tests output covers the obvious cases. A follow-up prompt focused specifically on edge case enumeration consistently finds 3 to 8 cases that the first pass missed. The workflow: after /tests generates the initial test suite, run a second prompt. 'For the function I just tested, enumerate 15 edge cases that the current tests do not cover. Categories to consider: input boundary conditions, null and undefined cases, empty collections, single-element collections, exactly-at-the-limit cases, off-by-one boundaries, unicode and non-ASCII input, very long input, concurrent access scenarios, error-during-iteration cases, partial-success cases, idempotency cases, retry-after-failure cases, time-zone and DST boundaries, and floating-point precision cases. For each, propose a test name and a one-line assertion. Rank by likelihood of being hit in production.' Copilot returns a ranked list. Filter to the ones that match realistic production input distributions; not every theoretically possible case deserves a test. Generate the tests for the kept cases with a follow-up /tests prompt. The two-step enumeration workflow is materially better than asking /tests to be exhaustive in a single prompt because the separated reasoning step produces more thorough enumeration than the bundled generation step.

Example prompt
After the initial /tests run, follow up: 'For the [function name] I just tested, enumerate 15 edge cases the current tests do not cover. Categories: boundary conditions, null/undefined, empty/single-element collections, off-by-one, unicode and non-ASCII, very long input, concurrent access, error during iteration, partial success, idempotency replay, retry after failure, time-zone and DST, floating-point precision, race conditions, resource exhaustion. For each: test name, one-line assertion, likelihood of being hit in production (low/medium/high). Then generate tests only for the high-likelihood cases.'
04

Generate fixtures and factories before generating tests that use them

Tests that inline their test data become unmaintainable as the test suite grows past 20 to 30 tests. Fixtures and factory functions centralize test data so changes propagate automatically. Before generating tests for a domain (orders, users, subscriptions, invoices), generate the fixture file first. The prompt: 'Generate a [entity]Factory in test/factories.ts following the existing factory conventions in this repo. Required fields: [list with type and default value generation strategy]. Relationship factories: [variant name and what it adds]. All factories should accept an override object as the last argument so individual tests can customize specific fields without rebuilding the whole object.' Copilot generates factory functions that integrate with Faker or a similar library for seeded realistic-looking data. For relational data (when you need consistent foreign keys across factories), the prompt extends: 'When relationships are needed, generate the related entity and link it via foreign key in the override.' Once the factory file exists, every subsequent test for that domain uses it via import. The investment of 10 minutes generating factories saves hours of fixture maintenance later. Test data should be 1 import line in each test, not 20 lines of inline construction.

Example prompt
'Generate a [entity]Factory in test/factories.ts following the existing factory style in this repo. Required fields with default generation: id (UUID v4), email (faker.email), name (faker.name.fullName), createdAt (faker.date.past 1 year), [domain-specific fields with defaults]. Relationship variants: [entity]WithOrders, [entity]WithSubscription, [entity]AtCartLimit. All factories accept an override object as the last argument. Export each as a named export. Do not include any logic that hits a real database or external service; factories should be pure data builders.'
05

Generate integration tests at the boundary with explicit realism level

Integration tests need a different prompt structure from unit tests because the unit of testing is the interaction between components. Select the integration boundary (the API route handler, the database transaction layer, the message-queue consumer, the file-upload pipeline) and prompt Copilot with three elements. First, the boundary semantics: 'POST /api/orders flows through OrderService.create which calls InventoryService.deduct and EventBus.publish.' Second, the realism level: 'Use a real Postgres instance via the testcontainers fixture in test/containers.ts. Use the in-process EventBus stub from test/event-bus-stub.ts. Do not mock InventoryService; let it run against the test Postgres.' Third, the cross-component behaviors to verify: 'order creation flows through inventory deduction and event publishing; rollback on payment decline restores inventory; concurrent order creation on the same SKU does not double-deduct; idempotency-key replay returns the original order without re-deducting.' The 3-element integration prompt produces tests that exercise the real integration paths. Without the realism level, Copilot defaults to heavily mocked tests that pass against mocks but fail in production. Without the cross-component behaviors, Copilot writes per-step assertions that miss the integration semantics. Integration tests are slower and more expensive to maintain than unit tests; cover the meaningful boundaries and let unit tests carry the rest.

Example prompt
'Integration test for the [boundary]. Boundary semantics: [request flows through component A, calls component B, publishes to component C]. Realism level: [real Postgres via testcontainers / in-process event bus stub / mocked external HTTP via msw]. Setup helper to reuse: [test/setup-server.ts or similar]. Cross-component behaviors to verify: (1) [end-to-end happy path], (2) [rollback on failure mid-flow], (3) [concurrent access at the shared resource], (4) [idempotency], (5) [observability assertion: the right event/log/metric was emitted]. Use existing factories for test data. Tear down state between tests with the existing transactional rollback helper.'
06

Run coverage-gap analysis with Copilot to find missing tests

Once the initial test suite is in place, coverage-gap analysis closes the loop. Run your coverage tool: Istanbul/nyc for Node.js, Coverage.py for Python, JaCoCo for Java, Cobertura for .NET, the built-in -coverprofile for Go, SimpleCov for Ruby. Export the per-file uncovered-line report. Paste it into Copilot Chat with the gap-analysis prompt. 'Coverage gap analysis for [file]. Uncovered lines: [paste line ranges]. Function source: [paste full source]. For each uncovered range: (1) what behavior is not tested? (2) is this behavior reachable in production or is it a defensive branch for impossible-in-practice conditions? (3) if reachable, what test would cover it and what fixtures would it use? (4) ranked priority based on production impact if the untested behavior fails.' Copilot returns a prioritized list. Filter for the production-reachable behaviors and generate tests for them with a follow-up /tests prompt. The discipline: not every uncovered line deserves a test. Defensive branches for impossible conditions can be tested with explicit unreachable() assertions or documented as deliberately uncovered. The goal is meaningful coverage, not 100% line coverage. For user-facing logic and error handling, aim for full coverage; for internal-only defensive code, accept lower coverage with clear documentation of why.

Example prompt
'Coverage gap analysis. File: [path]. Coverage tool output (uncovered lines with line numbers): [paste]. Function source code: [paste full source]. For each uncovered range: (1) what behavior is not tested in plain English? (2) is this reachable in production (e.g., a real input could hit it) or is it defensive code for impossible conditions? (3) if reachable, propose the test (name, fixtures, assertion). (4) priority based on production impact if the behavior failed silently (P0 critical / P1 important / P2 nice to have / P3 not worth testing). Output as a ranked list.'
07

Generate property-based tests for code with strong invariants

Property-based tests are the underused complement to example-based tests that catch entire categories of bugs example tests miss. For code with strong invariants (parsers, serializers, sort and dedupe functions, mathematical functions, round-trip transformations), property-based tests exercise hundreds or thousands of generated inputs in milliseconds and find edge cases human-authored examples would never cover. The libraries: fast-check for JavaScript and TypeScript, hypothesis for Python, proptest for Rust, jqwik for Java, ScalaCheck for Scala, QuickCheck for Haskell and other ML-family languages. The prompt pattern: identify the invariants ('the output is a permutation of the input,' 'the output is sorted,' 'applying twice equals applying once,' 'serialize then deserialize equals identity,' 'parse then unparse preserves semantic equivalence'), then ask Copilot to generate property-based tests. 'Property-based tests in [library] for [function]. Invariants to verify: (1) [property in plain English]. (2) [property]. (3) [property]. Use [library]'s arbitrary generators for the input types: [list types]. Aim for 200 generated cases per property. Use shrinking to produce minimal failing examples when properties fail.' Copilot generates the property tests with the appropriate generator setup. Run them; if they pass, you have meaningfully stronger coverage than example tests alone. If they fail, the shrinker reduces the failing case to a minimal example that often reveals a real bug.

Example prompt
'Property-based tests in fast-check for the [function]. Invariants to verify: (1) [e.g., output is always a permutation of input], (2) [e.g., output satisfies the ordering predicate], (3) [e.g., function is idempotent: applying twice equals applying once], (4) [round-trip: encode then decode equals identity]. Input generators: fc.array(fc.integer()), fc.string(), fc.record({...}). Number of runs per property: 200. Use fc.assert with shrinking enabled. Output the test file in the test/property/ directory following the existing property-test conventions in this repo.'
08

Review generated tests against the spec, not against the implementation

The single most important discipline in AI-generated testing is verifying that the tests assert the spec, not the implementation. Tautological tests (tests that pass because they mirror what the code does, regardless of whether the code is correct) provide zero regression value. The review checklist for every generated test: first, does the test name describe the intended behavior in user-meaningful terms? 'returns 401 when credentials are invalid' is good; 'calls bcrypt.compare with the right arguments' is implementation-coupled and brittle. Second, does the assertion match the spec? If you deliberately introduced a bug in the implementation, would the test catch it? Run a mental experiment: comment out the implementation, write a deliberately wrong implementation, and ask yourself whether the test would fail. If the test passes against the wrong implementation, it is tautological and needs rewriting. Third, are the fixtures realistic? Test data that does not match production distributions can pass tests that would fail on real input. Fourth, is the test isolated from other tests and from environmental state? Tests that depend on order, shared state, or wall-clock time become flaky. The 5-minute review per test catches tautological, brittle, and flaky tests before they enter the suite. A test suite full of tautologies is worse than no test suite because it gives false confidence; spend the review time.

Example prompt
Review checklist prompt for each generated test: '(1) Read the test name. Does it describe the intended user-visible behavior? Rewrite it if it describes implementation. (2) Read the assertion. Would this fail if the implementation were deliberately wrong in the relevant way? Mentally substitute an empty implementation and a deliberately wrong implementation; if either passes the test, the assertion is tautological. (3) Read the fixtures. Does the test data match production distributions? Are edge cases represented? (4) Read the setup and teardown. Does the test depend on order, shared state, or wall-clock time? If yes, isolate it.'

Common Mistakes That Produce Useless Tests

1. Generating tests from the implementation instead of from the spec

The tautological test trap. Copilot reads the function and writes tests asserting what the function does; the tests pass against the current code regardless of correctness. Always write the spec first, then ask Copilot to generate tests from the spec. The tests should fail if the implementation is wrong.

2. Accepting snapshot tests as the default assertion style

Snapshot tests look productive (green dots, high coverage) but provide zero regression value because the snapshot is regenerated on update. Prefer explicit assertions (toHaveTextContent, toHaveAttribute, specific structural checks) over toMatchSnapshot in almost every case. Reserve snapshots for genuinely stable structural outputs.

3. Skipping the edge case enumeration step

Default /tests output covers happy paths and obvious errors. The 3 to 8 edge cases that matter most in production (boundary conditions, null/undefined, concurrent access, time-zone, floating-point) require the two-step enumeration workflow. One-shot /tests produces incomplete coverage.

4. Inlining test data instead of using factories

Tests with 20 lines of inline fixture construction become unmaintainable past 30 tests in a domain. Generate factory functions first, then generate tests that import from the factory file. Test data should be 1 import line per test, not 20 lines of inline construction.

5. Heavy mocking that makes tests pass against mocks but fail against reality

If every external call is mocked, tests verify the mocks work, not the system. Prefer real dependencies in tests where possible: in-memory databases, real HTTP servers in test mode, real message brokers in containerized test mode. Reserve mocks for genuinely external services that cannot be locally hosted.

6. Asserting how instead of what (implementation-coupled tests)

The test 'calls bcrypt.compare with these arguments' is brittle; the test 'invalid password returns 401' is stable. Implementation-coupled tests break on every refactor even when behavior is unchanged. Always assert observable behavior (return value, side effects, calls to genuinely external systems), never internal structure.

7. Letting tests share state or depend on order

Tests that pass in one order and fail in another become flaky and erode trust in the suite. Use beforeEach for setup, afterEach for cleanup, and frozen time for time-sensitive logic. If a test depends on prior tests, you have a test architecture problem; isolate each test.

8. Chasing 100% line coverage as a goal

100% coverage encourages tests that exercise defensive branches for impossible conditions, which adds maintenance cost without preventing real bugs. Aim for full coverage on user-facing logic and error handling; accept lower coverage on internal defensive code with documentation of why it is acceptable.

Pro Tips (What Most Engineers Miss)

Generate the spec before the test. A spec is a plain-English description of behavior with input ranges and expected outputs. The spec lives in a comment block above the function or in a doc/spec.md file. Tests generated from the spec are dramatically less tautological than tests generated from the implementation.

Run the deliberate-wrong-implementation review. For every generated test, ask: if I substituted an empty implementation, would this test fail? If I substituted a deliberately wrong implementation, would this test fail? If either passes the test, the test is tautological and needs rewriting before merge.

Use the linked issue or PR as context for Copilot. 'Generate tests for this PR. The PR description specifies the acceptance criteria; use those as the spec.' Copilot's GitHub integration reads the issue and produces tests that match the acceptance criteria, which is materially better than asking it to invent the spec from the code.

For UI tests, invest in data-testid attributes in the application code. An hour spent adding test-ids to user-facing elements saves dozens of hours over the test suite's lifetime. Copilot uses test-ids automatically when they exist; the resulting tests are stable across UI redesigns.

For database integration tests, use testcontainers over in-memory databases. The 30-second startup cost is worth it because testcontainers exercises the real database engine, which catches dialect-specific bugs that in-memory mocks miss (Postgres window functions, MySQL collation behavior, SQL Server CTE recursion).

For property-based tests, start with idempotency and round-trip properties. Idempotency (applying twice equals applying once) and round-trip (encode then decode equals identity) are the easiest invariants to prove and catch a surprising number of real bugs in transformation and serialization code.

Use Copilot Edits for multi-file test refactors. When renaming a factory function used across 50 tests, or migrating from Jest to Vitest across a module, Copilot Edits handles the cross-file change coherently and runs the test suite to verify. Faster and safer than per-file find-and-replace.

For flaky tests, run the test 100 times in a loop and analyze the failure pattern. 'Run this test 100 times, count failures, capture the differing output between passes and failures.' The pattern in the failures usually points to the source of non-determinism (timing, ordering, shared state) which Copilot can then propose a fix for.

GitHub Copilot Testing Prompt Library (Copy-Paste)

Production-tested prompts organized by testing task. Run inside Copilot Chat with workspace indexing enabled. Replace bracketed variables with your specifics.

/tests with structured spec

'/tests for the [function name] I have selected. Purpose: [one sentence]. Behaviors to cover: (1) [behavior with expected outcome], (2) [behavior], (3) [behavior], (4) [edge case], (5) [error case]. Fixtures: use [factoryName] in [path]. Framework: match the existing conventions in this repo. Each test should be named to describe the behavior being asserted.'
'/tests for this React component. Use @testing-library/react with the render-with-providers helper in src/test/render.tsx. Cover: (1) renders the heading and primary CTA on initial mount, (2) shows the loading state while the query is in-flight, (3) shows the empty state when the data array is empty, (4) shows the error state when the query fails, (5) clicking the CTA fires the analytics event and navigates to /next. Assert with explicit queries (getByRole, getByLabelText), not snapshots.'

Two-step edge case enumeration

'For the [function] I just tested, enumerate 15 edge cases the current tests do not cover. Categories: boundary conditions, null/undefined, empty/single-element collections, off-by-one, unicode and non-ASCII, very long input, concurrent access, error during iteration, partial success, idempotency replay, retry after failure, time-zone and DST, floating-point precision, race conditions, resource exhaustion. For each: test name, one-line assertion, likelihood (low/medium/high). Then generate tests only for high-likelihood cases.'

Fixture and factory generation

'Generate a [entity]Factory in test/factories.ts following the existing factory style in this repo. Required fields with generation strategy: id (UUID v4), email (faker.email), name (faker.name.fullName), createdAt (faker.date.past 1 year), [domain fields]. Relationship variants: [entity]WithChildren, [entity]InState(state). All factories accept an override object as the last argument. Export each as a named export. Factories are pure data builders, no database or network calls.'

Integration tests at boundaries

'Integration test for POST /api/[resource]. Boundary semantics: request flows through [Controller -> Service -> Repository -> external publisher]. Realism level: real Postgres via testcontainers, in-process event-bus stub, msw for external HTTP. Setup helper: test/setup-server.ts. Cross-component behaviors to verify: (1) happy path end-to-end, (2) rollback on mid-flow failure preserves prior state, (3) concurrent requests on the same key do not double-process, (4) idempotency-key replay returns the original result, (5) observability assertion, the right event was published with the right payload.'

Coverage-gap analysis

'Coverage gap analysis. File: [path]. Coverage tool output: [paste uncovered line ranges]. Source code: [paste]. For each uncovered range: (1) what behavior is not tested? (2) is this reachable in production or defensive code for impossible cases? (3) if reachable, propose test name, fixtures to use, and the assertion. (4) priority (P0 critical / P1 important / P2 nice / P3 not worth). Output as a ranked list.'

Property-based testing

'Property-based tests in fast-check for the [function]. Invariants: (1) output is a permutation of input, (2) output satisfies the ordering predicate, (3) idempotent (applying twice = applying once), (4) round-trip (encode then decode = identity). Generators: fc.array(fc.integer()), fc.string(), fc.record({...}). Runs per property: 200. Use fc.assert with shrinking enabled.'
'Property-based tests in hypothesis for the [function]. Invariants to verify: [list]. Use @given with strategies: integers(min_value, max_value), text(), lists(), and custom composite strategies for domain entities. Use @example for boundary cases discovered in past bugs. Use settings(max_examples=300, deadline=None) for the slow integration property tests.'

E2E tests with Playwright/Cypress

'Playwright test for the [user journey]. Steps: (1) visit /[start path], (2) [action with input], (3) [action], (4) expect [assertion on URL/text/visibility], (5) [action], (6) expect [assertion]. Use the existing page-object pattern in tests/pages/. Prefer data-testid selectors over CSS classes or text. Use expect with auto-waiting; do not add manual sleeps. Tag the test with @smoke if it covers a critical path.'

Mock and stub generation

'Generate mocks for the external dependencies of this function using the existing mock style in this repo. External dependencies: [list with what they return on success and on error]. Mock library: [jest.mock / pytest-mock / Mockito / Moq matching the repo]. Mocks should be in __mocks__/ adjacent to the source or in test/mocks/ depending on the existing convention. For each mock, include a success-case factory and an error-case factory so individual tests can configure the mock state per test.'

Test review for tautologies

'Review this generated test for tautology risk. Test: [paste]. Implementation: [paste]. Run the checks: (1) would this test fail if the implementation were empty (returned undefined or default)? (2) would this test fail if the implementation were deliberately wrong in the relevant way? (3) does the assertion describe user-visible behavior or internal implementation structure? If the test passes the empty/wrong implementations, or asserts internals, rewrite it to assert observable behavior.'

Flaky test investigation

'Flaky test investigation. Test code: [paste]. Failure rate: [X]% over [Y] runs. Identify all sources of non-determinism: hardcoded timestamps, system clock dependencies, race conditions in async setup/teardown, shared global state, network or filesystem dependencies, RNG without seed, ordering assumptions across tests. For each: location in the test, fix that preserves test intent. Recommend running the test 100 times after each fix to verify failure rate dropped.'

Legacy code test retrofit

'Retrofit tests onto this untested module. Module: [path]. Step 1: write 2 reference tests by hand for the simplest public method, establishing the file location, fixture style, mock pattern, and naming convention. Step 2: extend with /tests for the remaining public methods following the reference pattern. Step 3: coverage-gap analysis after the suite is in place. Output: the 2 reference tests in the conventional location and a list of remaining methods to test.'

Frequently Asked Questions

Related Guides

Don't stop here

What to read next

Hand-picked guides our readers explore right after this one.