İçeriğe atla
Rosary Software
Yazılara dön

Machine-Readable Test Cards: A QA Standard Built for Humans and AI Agents

MDMert Dede15 Ağustos 20265 dk okuma
AIFor Developers
Bu sayfada

In 2026, leading a frontend team at a cryptocurrency exchange, I introduced a standard for how features are tested before they reach production. We call the core artifact a machine-readable test card. It is a functional test specification strict enough for an AI agent to execute over MCP, and clear enough that a human tester needs zero prior knowledge of the platform to run it.

The standard exists to solve the one problem I refuse to tolerate: features arriving in production less than 100% complete. In an exchange, an incomplete feature is not a cosmetic defect. It touches balances, orders, and market data. There is no acceptable failure budget.

Why standardize the testing phase at all?

The quiet cost in most teams is the handoff. Ad-hoc, verbal communication between developers and testers is lossy: context evaporates, edge cases go unmentioned, and every clarification is an interruption that breaks someone's flow state. The fix is not more meetings. The fix is written contracts, so the knowledge lives in the specification instead of the hallway.

What is a machine-readable test card?

Think OpenAPI and Swagger, but for functional testing: the same rigor as a machine contract, extended with UX expectations and verbose instructions, tailored to our platform and team structure.

When a feature branches from prod, its aim and affected area are defined up front. From that impact analysis we derive one list of test cards per feature, one card per surface or flow the feature touches, each with the coverage itself as a checklist and the responsible tester tagged. Every card states completely what to do, what to expect, and what not to expect: real input values, exact screens, boundary and negative cases.

Here is one card from a fiat withdrawal feature pass. The list is named `Fiat Withdrawal Functional Tests` and holds one card per affected surface; this card covers the amount entry step:

Card: Withdrawal, amount entry and confirmation
List: Fiat Withdrawal Functional Tests
Tester: @tester-name

Where: Wallet > Withdraw > Bank Transfer, amount step
Precondition: verified account, $X balance of exactly 1,250.00,
              daily limit 50,000 $X, minimum withdrawal 100 $X

Checklist:
[ ] Enter 100.00 (exact minimum). Continue is enabled, fee and
    net amount shown before confirmation
[ ] Enter 99.99. Continue stays disabled, hint under the field
    turns red and names the 100 $X minimum. Raise to 100.00,
    hint turns back to neutral
[ ] Enter 1,250.00 (full balance). MAX shortcut fills the same
    value, fee is deducted from the amount, never from balance
[ ] Enter 1,250.01. Inline "insufficient balance" error, no toast,
    no request fired (check network tab: zero POST calls)
[ ] Enter amount with locale separators (1.234,56 in a
    comma-decimal locale). Parsed correctly, wire value is
    plain decimal
[ ] Kill connectivity after tapping Confirm. Button shows inline
    spinner then a retryable error state. Balance display keeps
    the cached value, never resets to zero or dashes
[ ] Do NOT expect: a browser confirm() dialog, a page reload at
    any step, or the keyboard-jitter on input focus (fixed in the
    previous pass, regression check)

The result: a tester is effective from day one with no tribal knowledge of the platform or its development history. A defect that comes back follows the same discipline: Screen, numbered Steps with real values, Expected, Actual, Log. Expected plus Actual is the bug, and defines fixed:

Card: Withdrawal confirm keeps spinning on limit rejection
List: Blocking

Screen: Wallet > Withdraw > Bank Transfer, confirmation step

Steps:
  1. Log in with a verified account, $X balance above 60,000
  2. Enter 55,000.00 (above the 50,000 daily limit) and Confirm
  3. Observe the confirm button

Expected: Request is rejected, button returns to enabled state,
          inline error names the daily limit and the remaining
          allowance for today
Actual:   Button spins forever; user must reload the page.
          The 422 response is received but never handled
Log:      POST /withdrawals -> 422 LIMIT_EXCEEDED
          Console: Unhandled rejection (withdraw-form.tsx:148)

A card without reproducible steps is under-specified and bounces back. We do not guess.

How do AI agents execute test cards over MCP?

Because every card follows a fixed format contract, the cards are machine-readable in practice, not just in name. Our boards are wired to coding agents over the Model Context Protocol: an agent resolves the board to its repository, reads the card and its attachments as primary source material, reproduces from Steps, fixes, and hands the card back with a structured resolution comment.

That comment is itself a contract. It is written for the tester, who verifies through the running product and owes no familiarity with the repository, so it carries no diffs, no stack traces, no commit hashes. This is the comment the agent posted on the card above:

@tester-name

Verdict: fixed.

Cause: the failure only appeared on amounts above your daily
limit. The rejection reply from the server was ignored, so the
screen never left its waiting state.

Fix: limit rejections now release the button and show the limit
error inline. Amounts within the limit behave exactly as before.

How to test:
  1. Enter 55,000.00 and Confirm. Button returns to normal within
     two seconds; error names the 50,000 $X daily limit and your
     remaining allowance
  2. Boundary: enter exactly 50,000.00. Withdrawal proceeds to
     the success screen
  3. Negative: repeat step 1 with airplane mode on. You get the
     retryable connection error, not the limit message

Not verified: the same rejection path on the mobile app; that is
a separate card.

This is the part most teams miss about AI-era development. Agents do not need smarter prompts. They need the same thing junior testers need: an unambiguous written contract. Standardize for humans and the agents come for free.

Why not Gherkin?

The industry's default answer to machine-readable testing is Given/When/Then and a BDD toolchain. We deliberately skipped it. Gherkin adds a parser, a runner, and a maintenance surface, and it still cannot carry UX expectations, attachments, or tester assignment. A disciplined card format in the tracker the team already lives in gives an MCP-connected agent everything Gherkin would, with zero added toolchain and full readability for non-engineers.

Which flows deserve Playwright automation?

Automated coverage follows the stack: Maestro for our Expo React Native flows, Jest with React Testing Library in Next.js, Vitest in a few projects. Playwright E2E is deliberately not blanket-standardized. We invest in E2E automation only where a flow generates consistent revenue or competes directly on UX with rival exchanges. Everywhere else, the functional test pass is the coverage, at a fraction of the maintenance cost. Perfectionism is not automating everything. It is knowing exactly what each layer of testing is for.

Proving where a failure lives

The standard also solved our dependency on other teams. If a feature passes its full functional definition and the failing state is not reproducible in the client, the fault is demonstrably outside the frontend, not defensively claimed to be. Backend-caused defects are triaged to a dedicated lane and raised with their owners rather than worked around.

And because graceful degradation is part of every functional definition, an upstream failure never breaks the client: proper loading states, proper error states, correct cache behavior. Users see a handled state. The on-call sees an attributed cause. That is what peace of mind looks like when it is engineered rather than hoped for.

FAQ

Do machine-readable test cards replace QA engineers? No. They replace the onboarding tax and the verbal handoff. Humans define what correct means; the cards carry it; humans and agents both execute it.

What tool do the cards live in? Any tracker with an MCP server and checklists. The format contract matters, not the vendor.

Does this work outside crypto? Yes. The stakes made us build it, but the mechanism is domain-agnostic: any team shipping features across a developer-tester boundary benefits.

MD

Mert Dede

Developer

Profili görüntüle
Paylaş
Yorumlar
Loading...