Agent Skills: Write Unit Tests

Use when writing unit tests for code changes or new functionality. Triggers on "write tests", "add tests", "test this code".

UncategorizedID: v1-io/v1tamins/v1-write-tests

Install this agent skill to your local

pnpm dlx add-skill https://github.com/v1-io/v1tamins/tree/HEAD/plugins/v1tamins/skills/v1-write-tests

Skill Files

Browse the full folder contents for v1-write-tests.

Download Skill

Loading file tree…

plugins/v1tamins/skills/v1-write-tests/SKILL.md

Skill Metadata

Name
v1-write-tests
Description
Use when writing unit tests for code changes or new functionality. Triggers on "write tests", "add tests", "test this code".

Write Unit Tests

Add focused tests for changed behavior, bug seams, boundary cases, and regression risk using the project's existing test conventions.

Usage

Typical invocations:

  • Claude Code: /v1-write-tests [target]
  • Codex: invoke v1-write-tests from the skills menu or use $v1-write-tests [target]

Target (optional):

  • If not specified: tests cover the diff between current branch and main
  • If specified: tests cover the specified file or module

Examples:

/v1-write-tests                                    # Test all changes vs main
/v1-write-tests src/core/query.py                  # Test specific file

In Codex, the slash examples below map directly to $v1-write-tests ....

What It Does

1. Test Selection

  • Identify the changed public behavior from the diff, target file, or user request.
  • For bug fixes, reproduce the real failure pattern at the narrowest correct seam before asserting the fix.
  • Cover boundary and error paths that the changed behavior can realistically hit.
  • Add a test only when it can fail for a meaningful regression; skip coverage that only executes lines.
  • For each changed branch or state transition, prefer at least one assertion that would fail if that branch were removed, inverted, or bypassed.

2. Test Structure

  • Uses project's testing framework conventions
  • Names each test after the behavior and condition under test
  • Follows Arrange-Act-Assert pattern
  • Groups related tests logically

3. Test Cases Include

  • Happy path scenarios
  • Edge cases and boundary conditions
  • Error handling and exception cases
  • Appropriate mocking of external dependencies

4. Test Quality

  • Tests are independent and isolated
  • Tests are deterministic and repeatable
  • Each test has one primary reason to fail
  • Assertion messages or comments explain non-obvious invariants only
  • External APIs, wall-clock time, network calls, and shared mutable state are isolated behind fixtures, fakes, or test doubles unless the project explicitly uses integration tests for that layer

Style

  • Arrange-Act-Assert pattern
  • Descriptive test names (test_method_condition_expected_result)
  • Test user-visible UI text when it is the behavior; otherwise prefer roles, labels, states, and user outcomes over brittle copy assertions
  • Isolated and deterministic

What NOT to Do (Anti-Patterns)

Three rules, each explained below: assert on real behavior, not mocks; keep test-only code out of production classes; understand a dependency before mocking it.

Anti-Pattern 1: Testing Mock Behavior

Bad:

// Testing that mock exists, not real behavior
test('renders sidebar', () => {
  render(<Page />);
  expect(screen.getByTestId('sidebar-mock')).toBeInTheDocument();
});

Fix: Test real component or don't mock it. If you must mock, don't assert on mock elements.

Anti-Pattern 2: Test-Only Methods in Production

Bad: Adding destroy() to a class only because tests need cleanup.

// BAD: destroy() only used in tests
class Session {
  async destroy() { /* cleanup */ }
}

// In tests
afterEach(() => session.destroy());

Fix: Put cleanup utilities in test-utils/, not production code.

// GOOD: Test utility
// test-utils/cleanup.ts
export async function cleanupSession(session: Session) {
  const workspace = session.getWorkspaceInfo();
  if (workspace) await workspaceManager.destroyWorkspace(workspace.id);
}

// In tests
afterEach(() => cleanupSession(session));

Anti-Pattern 3: Mocking Without Understanding

Bad: Mocking "to be safe" without understanding what the test depends on.

// Mock breaks test logic - method had side effects test needed!
vi.mock('ToolCatalog', () => ({
  discoverAndCacheTools: vi.fn().mockResolvedValue(undefined)
}));

await addServer(config);
await addServer(config);  // Should throw - but won't!

Fix: Run test with real implementation first, then add minimal mocking.

Anti-Pattern 4: Incomplete Mocks

Bad: Mocking only fields you think you need.

const mockResponse = {
  status: 'success',
  data: { userId: '123' }
  // Missing: metadata that downstream code uses
};

Fix: Mirror the complete real API response structure.

Red Flags

  • Assertion checks for *-mock test IDs
  • Methods only called in test files
  • Mock setup is >50% of test
  • "Mocking just to be safe"
  • Test fails when you remove mock
  • Regression test cannot fail for the original bug because it bypasses the real call path

Gate Function

Before each mock, ask: "Do I understand what this test actually needs?"

| Question | If No... | |----------|----------| | What side effects does the real method have? | Don't mock yet | | Does this test depend on any of those side effects? | Mock at lower level | | Do I fully understand what this test needs? | Run with real impl first |

Notes

  • Only write tests when explicitly requested
  • Always run tests after creating them to verify they pass
  • Use the project-native test command. If no command is obvious, inspect package scripts, test config, Makefile, or repo docs before guessing.