Eval-Driven Vibe Coding: Unit Tests for
Build deterministic evaluation suites and unit tests for AI coding agents to catch prompt drift and regressions before shipping to production.
Vibe coding enables developers to prototype features at unprecedented speed. By delegating implementation details to coding assistants, engineers can build working systems in hours. However, as applications scale beyond weekend experiments, the primary bottleneck shifts from generating code to preventing regressions.
When an AI assistant refactors a module, introduces a new feature, or updates a database query, manual code review often misses silent behavioral regressions. A subtle change in system prompt wording, a model upgrade from a provider, or context window truncation can cause an agent to omit error handling, invent non-existent arguments, or break existing API contracts.
The solution is not to slow down development with bureaucratic manual verification. The solution is eval-driven vibe coding: creating lightweight, automated unit tests and evaluation harnesses specifically designed to grade agent behavior. Here is how to construct deterministic evaluation suites for AI agents in production.
Contents
- The Pitfalls of Unchecked Vibe Coding
- Architecture of an Agent Unit Test
- Deterministic Checks vs Semantic Grading
- Building a Minimal Python Eval Harness
- Mocking External Tools and Sandboxes
- Integrating Evals into CI/CD Gates
- FAQ
The Pitfalls of Unchecked Vibe Coding
Most vibe coding workflows follow a predictable pattern:
- Provide an initial prompt describing a feature.
- Allow the AI agent to generate or modify multiple files.
- Refresh the browser or run the application once.
- If the happy path works, commit and push to main.
While this flow works for standalone scripts, it introduces systemic fragility into complex codebases. Models are probabilistic engines. An agent that correctly preserved authentication middleware yesterday might rewrite route definitions today in a way that bypasses token verification.
Furthermore, agent tools introduce unexpected side effects. Without rigorous assertions on tool execution payloads, an agent might execute destructive terminal commands or write files outside the repository root. For foundational isolation practices, review our guide on MCP Server Hardening for Vibe Coders.
To maintain engineering velocity without sacrificing code reliability, teams must codify intended behavior into executable evals before delegating tasks to agents.
Architecture of an Agent Unit Test
Traditional unit testing asserts that a specific function output matches an expected return value given fixed inputs. Agent evaluation, by contrast, tests both the intermediate trajectory (tool calls, reasoning steps, resource reads) and the final output state (generated code, file diffs, test suite pass rates).
A complete agent test case consists of four distinct components:
- System Prompt & Task Context: The baseline instructions, repository rules, and specific task prompt delivered to the agent runtime.
- Mock Environment: A sandboxed workspace containing the files, mocked APIs, or simulated shell commands the agent interacts with.
- Execution Trajectory Capture: A structured trace recording every tool call, payload argument, and intermediate thought generated during the session.
- Grading Harness: A multi-layered evaluation pipeline combining strict deterministic assertions with model-graded semantic checks.
By isolating these layers, you can verify whether an agent successfully solved an issue without manually reproducing the environment every time.
Deterministic Checks vs Semantic Grading
A common mistake in agent testing is attempting to grade everything with an LLM judge. Model-as-a-judge approaches are valuable for assessing stylistic readability and prose nuance, but they introduce non-determinism, cost, and latency.
High-velocity teams separate evaluations into two tiers:
Tier 1: Deterministic Invariants
These checks run in milliseconds without calling external model APIs:
- Syntax & Compilation: Does the generated file compile or parse without errors (e.g.,
python -m py_compileortsc --noEmit)? - Tool Schema Conformance: Did the agent invoke tools with strictly valid JSON matching required parameter schemas?
- Pre-Existing Test Suite: Does the project test suite pass cleanly (
pytestornpm test) after the agent commits its modifications? - Linter & Security Rules: Are banned patterns, hardcoded secrets, or invalid imports flagged by static analysis?
Tier 2: Semantic Grading
These checks run only after Tier 1 tests succeed:
- Instruction Adherence: Did the agent satisfy all edge cases requested in the brief without hallucinating extraneous features?
- Code Conciseness: Did the model introduce bloated boilerplate or maintain idiomatic, minimal implementations?
- Explanation Quality: If the agent provided documentation or comments, are they clear, accurate, and free of marketing fluff?
For governance patterns on merging agent pull requests, consult our framework on Human Review Gates.
Building a Minimal Python Eval Harness
Building an automated eval suite does not require enterprise orchestration platforms. You can build a robust, reproducible grading engine using standard Python libraries and pytest.
Below is a self-contained evaluation runner that tests whether an agent correctly refactors a legacy utility function without breaking edge cases:
import jsonimport pytestfrom dataclasses import dataclassfrom typing import List, Dict, Any@dataclassclass EvalCase: name: str prompt: str expected_tools: List[str] must_contain_tokens: List[str] forbidden_tokens: List[str]TEST_CASES = [ EvalCase( name="secure_token_generator", prompt="Refactor generate_token() to use secrets module instead of random.", expected_tools=["read_file", "write_to_file"], must_contain_tokens=["import secrets", "secrets.token_hex"], forbidden_tokens=["import random", "random.randint"], ), EvalCase( name="database_retry_backoff", prompt="Add exponential backoff to query_postgres() handling connection drops.", expected_tools=["replace_file_content"], must_contain_tokens=["retry_count", "time.sleep", "backoff_factor"], forbidden_tokens=["while True:\n pass"], )]def simulate_agent_run(case: EvalCase) -> Dict[str, Any]: """ Mock agent execution runner. In production, this invokes your local agent runtime or CLI harness in a temporary workspace. """ if case.name == "secure_token_generator": return { "invoked_tools": ["read_file", "write_to_file"], "generated_diff": ( "- import random\n" "+ import secrets\n" "- return str(random.randint(1000, 9999))\n" "+ return secrets.token_hex(16)\n" ), "exit_code": 0 } return { "invoked_tools": ["replace_file_content"], "generated_diff": "retry_count = 3\nbackoff_factor = 2\ntime.sleep(backoff_factor)", "exit_code": 0 }@pytest.mark.parametrize("case", TEST_CASES, ids=lambda c: c.name)def test_agent_eval_invariants(case: EvalCase): result = simulate_agent_run(case) # 1. Assert successful exit code assert result["exit_code"] == 0, f"Agent run failed for {case.name}" # 2. Assert mandatory tools were utilized for tool in case.expected_tools: assert tool in result["invoked_tools"], ( f"Expected tool '{tool}' not called in {case.name}" ) # 3. Assert mandatory code tokens are present in output diff = result["generated_diff"] for token in case.must_contain_tokens: assert token in diff, f"Missing required token '{token}' in {case.name}" # 4. Assert forbidden anti-patterns were avoided for token in case.forbidden_tokens: assert token not in diff, f"Found forbidden token '{token}' in {case.name}"This harness executes deterministically in local terminal sessions. When upgrading system prompts or routing tasks through Dynamic Subagent Dispatch and Supervisor Graphs, running this suite immediately identifies regressions before manual testing.
Mocking External Tools and Sandboxes
Agents that interact with databases, cloud infrastructure, or external APIs cannot be tested against production environments. Running unverified code against live backends risks data loss and unexpected rate limits.
To build reliable eval environments:
- Use Ephemeral Scratch Directories: Generate temporary working directories using
tempfile.TemporaryDirectory(). Copy only the targeted source files into the sandbox, execute the agent run, evaluate outputs, and immediately tear down the directory. - Simulate Tool Responses: Create deterministic mock servers for MCP tools. If an agent executes a SQL query tool, return fixed JSON fixture responses rather than executing live queries.
- Snapshot and Rollback: For multi-step agent tasks touching persistent stores, use isolated database containers or automated transaction rollbacks. For database backup patterns, refer to our walkthrough on Zero-Downtime PostgreSQL Backups.
# Example isolated eval test invocation in terminalpytest tests/agent_evals/ -v --tb=shortIntegrating Evals into CI/CD Gates
The primary advantage of eval-driven development comes from automating verification in pull requests and git hooks.
Configure a pre-push hook or GitHub Actions workflow that:
- Executes unit tests against all modified agent prompts and configuration files (
AGENTS.md,CLAUDE.md, system prompts). - Runs static analysis and type checks across newly generated code before human review.
- Blocks automatic merging if an agent run produces code that violates project formatting or architectural boundaries.
By shifting verification from subjective manual inspection to automated regression suites, developers can vibe code fearlessly at scale.
FAQ
- What is the difference between a traditional unit test and an agent eval?
Traditional unit tests verify deterministic code by comparing function outputs against static assertions. An agent eval tests the behavior of non-deterministic AI models, asserting that prompt instructions produce correct tool selections, valid code diffs, and passing test suites within acceptable token and execution limits.
- When should I use an LLM-as-a-judge instead of static assertions?
Use static assertions (syntax checks, linters, pre-existing unit test suites) for at least 80% of your evaluation pipeline. Reserve LLM-as-a-judge checks exclusively for qualitative criteria that cannot be easily parsed with regex or compiler ASTs, such as documentation readability, prompt tone, and semantic completeness.
- How many eval cases do I need before trusting an agent workflow?
Start with 5 to 10 high-value failure cases. Whenever an agent produces a hallucinated import, an incorrect database mutation, or a broken edge case in production, convert that specific incident into a permanent eval case. Over time, this regression suite becomes your most valuable defense against prompt degradation.