Skip to main content

Building a Testing Agent with OpenCode (Opinionated Walkthrough)

9 min read

Quick disclaimer before we start

Okay so this is basically my messy notes from experimenting with OpenCode for test generation. I'm not saying this is the way to do it, it's just what I tried and what kinda worked for me.

Do I think AI should write all your tests? Nah. Do I trust it blindly? Hell no. But I was curious: could OpenCode actually generate decent tests if I gave it better context?

The problem I was dealing with

So you're already using OpenCode for development purposes. Features get built, bugs get squashed, refactoring happens. Cool. But the unit tests? Kinda all over the place.

Your codebase has okay coverage, but it's inconsistent af. Some components have solid tests, others have literally nothing. When AI generates tests, it's sometimes genius, sometimes testing stuff that doesn't even exist.

The real issue? Lack of specialized context for testing tasks.

Here's how I structured an OpenCode project specifically for testing workflows. This isn't a copy-paste tutorial. It's more like a framework for thinking about how to organize your own setup based on what you or your team actually needs.

note

All the implementation stuff here is based on the official OpenCode docs. If something looks weird or outdated, check there first.

Prerequisites: You're already using OpenCode and get the basics (primary agents, subagents, @ mentions). If not, see the documentation. Please understand that this post is about structuring a testing-specific agent system.

The Obvious

generic agents = generic tests

When I prompted OpenCode's default Build agent to "generate tests for this component," it worked—but the results were inconsistent. Without testing-specific context:

  • Sometimes wrong testing framework
  • Over-mocking (mocking internal utility functions that didn't need it)
  • Under-testing (missing edge cases)
  • Inconsistent patterns (different test structures everywhere)

The default Build agent is a generalist. It does everything: features, refactoring, debugging, testing. When every task gets the same context, nothing gets specific context.

What if testing had its own specialized agent system, separate from general dev work?

Approach

I decided to try building testing-focused agents with clear separation of concerns:

  1. Primary testing agent - The Runner
  2. Specialized subagents - Handle specific tasks
  3. Shared context - Testing guidelines everyone references

This way, the Build agent stays focused on dev. Testing agents stay focused on testing. Each gets exactly the context it needs.

Disclaimer

The examples below are just examples. Your team's testing needs are gonna be different. The framework matters more than the specifics.

Step 1: Document your testing patterns first

Before building agents, I needed a testing knowledge base. Created docs/TESTING_GUIDELINES.md as the "source of truth" for how my team tests.

Since OpenCode agents can read project files directly, they access this via the read tool when generating tests.

Here's a condensed template (customize for your actual needs):

docs/TESTING_GUIDELINES.md
# Testing Guidelines

## Tech Stack
- Framework: Vitest
- Mocking: vi.fn() and vi.mock()

## Core Principles
1. Test behavior, not implementation
2. Mock external dependencies only
3. Keep tests focused (max 10 per file)

## Naming Convention
describe('Component', () => {
describe('WHEN condition', () => {
it('SHOULD result', () => {});
});
});

## Example Test
[Include one perfect example from your codebase]

Key point: This isn't comprehensive documentation. It's the minimum viable context to guide AI test generation. Add more as you figure out what the agents actually need.

Step 2: Customize your AGENTS.md

OpenCode uses AGENTS.md to understand your project. I ran /init to generate one:

opencode
# In the TUI:
/init

This analyzes the project and creates AGENTS.md with basic structure info. But it's generic—I needed testing-specific context.

What /init gave me (simplified):

AGENTS.md
# My TypeScript Project

## Project Structure
- `src/` - Source code
- `dist/` - Compiled output

## Tech Stack
- TypeScript with strict mode
- Node.js runtime

What I added:

AGENTS.md
# My TypeScript Project

[... existing structure ...]

## Testing Standards
- **Framework**: Vitest (NOT Jest)
- **File Convention**: Co-locate tests as `*.test.ts`
- **Naming**: BDD-style (WHEN/SHOULD)
- **Mocking**: External APIs only

See docs/TESTING_GUIDELINES.md for detailed patterns.

This makes sure every agent understands the testing setup. Customize based on your team's actual conventions, don't just copy mine.

Step 3: Design the agent architecture

I created three-agents as the baseline. Individual agents live in .opencode/agents/ as separate markdown files.

Primary Agent:

.opencode/agents/tester.md
---
description: Testing coordinator
mode: primary
---

Orchestrate testing workflows:
1. Read AGENTS.md and docs/TESTING_GUIDELINES.md
2. Delegate to @unit-tester or @test-data-generator
3. Run tests via npm test
4. Fix failures with subagent help

Subagent for unit tests:

.opencode/agents/unit-tester.md
---
description: Unit test generator
mode: subagent
tools:
bash: false
---

Generate unit tests:
- Follow TESTING_GUIDELINES.md patterns exactly
- Max 10 tests per file
- Focus on behavior, not implementation
- Cannot run bash (tester agent handles that)

Subagent for test data:

.opencode/agents/test-data-generator.md
---
description: Creates test fixtures
mode: subagent
tools:
bash: false
---

Create reusable mock data in __tests__/fixtures/
- Factory functions for common entities
- Flexible via override parameters
note

These are simplified examples. Your agents need instructions specific to your framework, patterns, and edge cases. The architecture is what matters here. But for more detailed options, see the offical docs

Key points:

  • Primary agent has bash access, coordinates everything
  • Subagents can only read/write files, handle specific tasks
  • All agents reference both AGENTS.md (project) and TESTING_GUIDELINES.md (testing patterns)
  • Tool restrictions = security (subagents can't execute random commands)

Step 4: Configure automatic context loading

I needed agents to automatically access testing guidelines. Two options:

Option 1: opencode.json (what I used):

.opencode/opencode.json
{
"instructions": ["docs/TESTING_GUIDELINES.md"],
"agent": {
"tester": {
"permission": {
"bash": {
"npm test *": "allow",
"*": "ask"
}
}
}
}
}

This loads guidelines into every agent's context automatically. The permission config lets @tester run test commands without asking for approval each time.

Option 2: Explicit instructions in AGENTS.md:

AGENTS.md
## Testing
When working on tests, read @docs/TESTING_GUIDELINES.md
using your Read tool before generating any test code.

I preferred Option 1 (automatic loading) to avoid relying on agents remembering stuff.

Step 5: Set up permissions properly

Testing agents also need specific permissions. In your config:

.opencode/opencode.json
{
"agent": {
"tester": {
"permission": {
"bash": { "npm test *": "allow", "*": "ask" },
"task": { "unit-tester": "allow", "test-data-generator": "allow" }
}
},
"unit-tester": {
"mode": "subagent",
"tools": { "bash": false }
},
"test-data-generator": {
"mode": "subagent",
"tools": { "bash": false }
}
}
}

What this does:

  • @tester can run test commands on its own
  • @tester can delegate to subagents via task tool
  • Subagents cannot execute bash commands (security boundary)

How to actually use this

Switch to testing agent with Tab key or invoke directly:

@tester Generate tests for @src/user-service.ts

What happens behind the scenes:

  1. @tester reads source file and guidelines
  2. Delegates to @unit-tester via task tool
  3. @unit-tester generates test file
  4. @tester runs npm test
  5. If failures happen, @tester works with @unit-tester to fix

Navigate between parent/child sessions with <Leader>+Right/Left.

You can also make a custom command:

.opencode/commands/test.md
---
description: Generate tests for a file
agent: tester
---
Generate tests for $1 following project guidelines.

Usage: /test src/user-service.ts

Or invoke subagents directly for fixtures:

@test-data-generator Create fixture for User model

Refining the guidelines over time

As I used the system, I kept updating docs/TESTING_GUIDELINES.md based on what worked and what didn't:

Added anti-patterns:

docs/TESTING_GUIDELINES.md
## Anti-Patterns
- Don't mock simple utility functions.
- Don't test getter/setter methods.
- Don't use `new Date()` in tests, use vi.setSystemTime() instead.

Added framework-specific stuff:

docs/TESTING_GUIDELINES.md
## React Testing
- Use React Testing Library
- Query by role, not class/ID
- Test user interactions, not component state

The more specific the guidelines, the better the output. This is iterative, your first version won't be perfect and that's expected.

What I learned (tbh)

Context: A well-documented TESTING_GUIDELINES.md is way more valuable than clever prompts. The guidelines persist across all interactions.

Agent specialization scales: As your project grows, you can add more specialized subagents (e.g., @integration-tester) without messing up existing workflows.

Extensions I thought about

Integration test agent:

.opencode/agents/integration-tester.md
---
description: Integration test generator
mode: subagent
---
Generate integration tests for APIs and database interactions.
Follow TESTING_GUIDELINES.md patterns for integration tests.

Coverage analyzer:

.opencode/agents/coverage-analyzer.md
---
description: Test coverage analysis
mode: subagent
tools:
bash: true
---
Analyze test coverage, identify gaps, suggest specific test cases.
Run: npm run coverage

You'd document these in AGENTS.md:

AGENTS.md
## Testing Agents
- @tester (primary): Coordinates testing
- @unit-tester (subagent): Unit tests
- @integration-tester (subagent): Integration tests
- @test-data-generator (subagent): Fixtures

Your needs will be different. Add agents based on actual needs, this post is merely a reference.

Final thoughts

Yeah, it's experimental. It's opinionated. It won't fit your needs exactly as-is. At least we figured out that AI agents need clear boundaries and specific context. That structure comes from:

  1. Context hierarchy: Project info (AGENTS.md) + testing specifics (TESTING_GUIDELINES.md) + role definitions (agent files)
  2. Agent specialization: Primary orchestrator + focused subagents
  3. Permission boundaries: Who can run what
  4. Explicit configuration: Don't rely on agents to "figure it out"
note

The examples here won't work if you just copy-paste them into your project. You need to specify your actual testing framework, mocking strategy, naming conventions, edge cases, and probably a bunch more stuff I haven't thought of.

Think of this as an inspiration for organizing OpenCode around testing workflows, not an actual solution. Your setup will look different from mine. That's literally the point.

All details based on the OpenCode docs as of when I'm posting this. Check there for updates.

Anyway, hope this helps someone avoid the trial-and-error I went through. If you've got a better approach, do lmk.