> For the complete documentation index, see [llms.txt](https://learning.contextqa.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://learning.contextqa.com/testing-with-ai-coding-agents/functional-testing.md).

# Functional Testing with AI Agents

Turn business requirements into functional tests with Claude Code, Cursor, or Codex, then create and verify ContextQA cases through MCP.

## Quick answer

Start with a requirement, turn it into an observable expected outcome, and ask your coding agent to test that behavior at the most useful layer. Functional testing describes **what the software must do**, not a particular framework: a functional rule can be checked through a function, an API, or a browser. ContextQA MCP can turn requirements into cases, but you still need to review their assertions and inspect real execution results.

This guide follows a report-export permission rule from a decision table to code-level checks and a ContextQA browser workflow. The example is illustrative; it does not describe a particular customer's authorization model.

## Functional, integration, and end-to-end are different dimensions

A functional test asks whether an approved user can export a ready report and whether a disallowed request is denied. An integration test asks whether real connected components cooperate correctly. An end-to-end test asks whether the full selected journey works. One test may satisfy more than one description.

Use fast code-level checks for numerous rule combinations, real API tests for authorization enforcement, and selected browser flows for the user's experience. A disabled button is useful UX evidence, but it cannot establish that an API rejects a forbidden request. See [unit](/testing-with-ai-coding-agents/unit-testing.md), [integration](/testing-with-ai-coding-agents/integration-testing.md), and [end-to-end testing](/testing-with-ai-coding-agents/end-to-end-testing.md) for those layers.

## Prerequisites

You need an agreed requirement, named roles and states, representative disposable data, the existing test command, and an environment where you can verify the outcome. Record unresolved policy questions instead of asking the agent to invent them.

For our example, report export is allowed only when the requester belongs to the report's tenant, the role is `admin` or `analyst`, and the report state is `ready`. A `viewer` cannot export. Tenant checks happen before role or readiness checks so a cross-tenant request does not disclose a report's state.

The small runnable example uses Node.js 24 or later and its built-in test runner. No ContextQA access is needed for the local exercise. [Node test runner](https://nodejs.org/api/test.html).

## 1. Make a decision table before generating tests

| Requirement | Given                                   | Expected outcome                      |
| ----------- | --------------------------------------- | ------------------------------------- |
| F-01        | Same tenant, analyst, ready report      | Export permitted                      |
| F-02        | Same tenant, admin, ready report        | Export permitted                      |
| F-03        | Same tenant, viewer, ready report       | Denied; no file produced              |
| F-04        | Same tenant, analyst, building report   | Denied as not ready                   |
| F-05        | Different tenant, admin, ready report   | Denied without revealing report state |
| F-06        | Same tenant, unknown role, ready report | Denied by default                     |

Discuss ambiguous states such as cancelled, failed, deleted, and expired reports. Determine what should happen if permission is revoked between opening the page and clicking Export. These are product questions; fluent generated steps are not a substitute for an agreed answer.

## 2. Ask the agent to preserve the business rule

Use this prompt with [Claude Code](/testing-with-ai-coding-agents/claude-code-testing.md), [Cursor](/testing-with-ai-coding-agents/cursor-testing.md), or [Codex](/testing-with-ai-coding-agents/codex-testing.md):

```
Use requirements F-01 through F-06 as the expected behavior. Inspect
our implementation and existing tests before editing anything.

Map each requirement to a test and explain the chosen layer. Add fast
policy tests for the combinations, API tests for actual enforcement,
and one allowed plus one denied browser journey where the environment
supports them. Do not invent unspecified status codes or UI messages.

For every denied case, verify the forbidden action did not occur.
A denial is a passing test when denial is the expected behavior.
For allowed export, verify file contents and tenant/report identity,
not only a download notification. Keep secrets and real customer data
out of prompts, screenshots, and generated fixtures.

Run the available tests. Report requirement ID, command or result ID,
observed outcome, and pass/fail/blocked for each. Do not mark a generated
case executed or replace a missing assertion with an agent opinion.
```

This makes the handoff reviewable for a QA manager or product owner who does not use the agent's editor. Each result answers a requirement rather than merely listing how many scripts were generated.

## 3. Run a small policy example

Create `report-policy.mjs`:

```js
export function exportDecision({ sameTenant, role, state }) {
  if (sameTenant !== true) return 'forbidden';
  if (!['admin', 'analyst'].includes(role)) return 'forbidden';
  if (state !== 'ready') return 'not-ready';
  return 'allowed';
}
```

Create `report-policy.test.mjs` beside it:

```js
import test from 'node:test';
import assert from 'node:assert/strict';
import { exportDecision } from './report-policy.mjs';

const cases = [
  ['F-01', true, 'analyst', 'ready', 'allowed'],
  ['F-02', true, 'admin', 'ready', 'allowed'],
  ['F-03', true, 'viewer', 'ready', 'forbidden'],
  ['F-04', true, 'analyst', 'building', 'not-ready'],
  ['F-05', false, 'admin', 'ready', 'forbidden'],
  ['F-06', true, 'unknown-role', 'ready', 'forbidden'],
  ['F-05 privacy', false, 'admin', 'building', 'forbidden'],
];
for (const [id, sameTenant, role, state, expected] of cases) {
  test(id, () => {
    assert.equal(exportDecision({ sameTenant, role, state }), expected);
  });
}
```

Run:

```bash
node --test report-policy.test.mjs
```

This is a functional decision table executed at the unit level. It verifies the illustrated policy function, not a deployed application's authentication or storage. To test your real product, import the policy it actually calls, then use API and browser checks to establish enforcement. Do not accept a separate test-only policy function as evidence about production code.

## 4. Verify the action, its absence, and the artifact

For an allowed export, assert the file type, expected headers or schema, known row values, tenant identity, and report identifier. Use a small seeded dataset with an exact expected result. “File exists” can pass even if the file contains an error page, an empty body, or another user's records.

For a denied export, verify the documented denial and the absence of the forbidden side effect. Decide how to observe that reliably: no successful export response, no new export record, or a bounded check that no download starts. The strongest check depends on your architecture. If the test cannot inspect the required state, mark that part unverified.

Test stale sessions and permissions changed mid-flow separately. Keep setup assertions clear so a missing seeded report is diagnosed as a precondition problem rather than a product authorization failure.

## 5. Create corresponding ContextQA cases through MCP

[Set up ContextQA MCP](/mcp-server/installation-and-setup.md), confirm the correct workspace, and inspect available tools. The documented `generate_tests_from_requirements` tool accepts `requirements_text`. Supply the decision table, approved staging URL, role fixtures, and exact expected behavior in that requirements text. Generation does not establish execution or coverage quality. [Generation reference](/mcp-server/tool-reference/test-generation.md).

```
Create ContextQA cases from requirements F-01 through F-06 for our
approved staging report workflow. Use the existing dedicated role
accounts and seeded report references; do not print their passwords.
Inspect and show the generated steps before running them.

Start with F-01 allowed analyst export and F-03 denied viewer export.
The allowed case must verify the required file evidence. The denied
case must verify the prohibited export did not succeed. Identify any
artifact check not supported by the available tools rather than omitting
it silently. Use the approved alternative check or mark it blocked.

Execute the reviewed cases. Retrieve actual result IDs and per-step
assertions. Preserve failures and report each requirement's outcome.
```

Use `get_test_case_steps` to review the created cases, then the documented execution/status/result tools to obtain evidence. A terminal completion state is not itself a passing assertion. See [case management](/mcp-server/tool-reference/test-case-management.md) and [execution results](/mcp-server/tool-reference/execution-and-results.md).

## Review and debugging

When expected and actual behavior disagree, establish which requirement is authoritative before changing code or tests. A screenshot of “Access denied” is a good result for the viewer case and a defect for the allowed analyst case. Keep that interpretation attached to the role and seeded state.

If a locator changes, repair how the test finds the same intended element. Do not remove the authorization assertion or broaden the target to a different action. Retries, generated explanations, and self-healing proposals should retain the original evidence and expected outcome.

## Common questions

### Does functional testing mean browser testing?

No. It checks required behavior at the layer that exposes the relevant risk. Browser testing is one execution method.

### Can AI derive tests from a ticket?

It can propose cases, but incomplete acceptance criteria remain incomplete. Review roles, state, boundaries, and expected results before treating the generated cases as coverage.

### How do we keep the tests portable?

Store native tests and requirement IDs in Git. For ContextQA cases, follow [export and portability](/testing-with-ai-coding-agents/export-and-portability.md), inspect assertions and fixtures after export, and rerun them before claiming equivalent coverage.

**Next step:** [Connect ContextQA MCP](/mcp-server/installation-and-setup.md) to translate one decision table into reviewable cases. For a demonstration using your roles and report workflow, [book a functional-testing demo](https://contextqa.com/book-a-demo/?utm_source=learning.contextqa.com\&utm_medium=referral\&utm_campaign=docs_demand_gen\&utm_content=functional-testing).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://learning.contextqa.com/testing-with-ai-coding-agents/functional-testing.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
