Agent skill
testing-automation
Testing patterns with Bun test runner, coverage thresholds, mocking, and CI/CD integration. Use when writing tests, organizing test files, or setting up quality gates.
Install this agent skill to your Project
npx add-skill https://github.com/pwarnock/liaison-toolkit/tree/main/.skills/testing-automation
Metadata
Additional technical details for this skill
- author
- liaison-toolkit
- version
- 1.0
- keywords
- testing, bun, coverage, mocking, ci-cd, quality
SKILL.md
Testing Automation
Patterns and best practices for testing TypeScript projects with Bun test runner, including coverage thresholds, mocking strategies, and CI/CD integration.
When to use this skill
Use this skill when:
- Writing unit tests or integration tests
- Organizing test file structure
- Configuring test coverage thresholds
- Mocking dependencies for CLI testing
- Setting up CI/CD test pipelines
- Debugging failing tests
Bun Test Runner Basics
Running Tests
# Run all tests
bun test
# Run tests in watch mode
bun test --watch
# Run tests with coverage
bun test --coverage
# Run specific test file
bun test path/to/specific.test.ts
# Run tests matching pattern
bun test --pattern "**/*command.test.ts"
Test File Organization
// src/cli.test.ts (test file alongside source)
import { describe, it, expect, beforeEach, afterEach } from 'bun:test';
describe('CLI Command', () => {
beforeEach(() => {
// Setup before each test
});
afterEach(() => {
// Cleanup after each test
});
it('should parse command arguments correctly', async () => {
const result = parseCommand(['create', 'test']);
expect(result.name).toBe('test');
});
});
Test Types
// Unit tests - test individual functions
it('should validate skill name format', () => {
expect(validateSkillName('test-skill')).toBe(true);
expect(validateSkillName('Invalid_Name')).toBe(false);
});
// Integration tests - test workflows
it('should create skill with all subdirectories', async () => {
await createSkill('test-skill');
const skillExists = await fs.exists('.skills/test-skill/SKILL.md');
expect(skillExists).toBe(true);
});
// Smoke tests - basic functionality tests
it('should run without errors', async () => {
const result = await executeCommand(['list']);
expect(result.exitCode).toBe(0);
});
Test Coverage
Coverage Thresholds
From qa-subagent configuration in agents/qa-subagent.json:
// Target: 80% code coverage
const coverageThreshold = 80;
Generating Coverage Reports
# Run tests with coverage
bun test --coverage
# Coverage output in coverage/ directory
# - coverage/index.html - HTML report
# - coverage/coverage-final.json - JSON for CI
Enforcing Coverage in CI
# .github/workflows/test.yml
- name: Check coverage
run: bun test --coverage
- name: Verify threshold
run: |
COVERAGE=$(cat coverage/coverage-summary.json | jq '.total.lines.pct')
if (( $(echo "$COVERAGE < 80" | bc -l) )); then
echo "Coverage $(COVERAGE)% is below 80%"
exit 1
fi
Mocking and Stubbing
Mocking File System
import { mockFS } from 'bun:test/mock';
mockFS({
'.skills/test-skill/SKILL.md': 'content here',
}, async () => {
await createSkill('test-skill');
const content = await fs.readFile('.skills/test-skill/SKILL.md', 'utf-8');
expect(content).toBe('content here');
});
Mocking CLI Input
import { mockProcess } from 'bun:test/mock';
mockProcess({
argv: ['create', 'test-skill'],
stdin: 'y\n', // Simulate user input
});
await executeCommand();
// Verify command behavior with mocked input
Mocking External Services
import { mock, spyOn } from 'bun:test';
// Mock Context7 API
const mockContext7 = mock(() => ({
resolveLibrary: async (name: string) => ({ id: name, docs: '...' }),
}));
// Use mock in test
const result = await mockContext7.resolveLibrary('react');
expect(result.docs).toBeDefined();
CLI Testing Patterns
Capturing Output
import { stdout, stderr } from 'bun:test';
const spyStdout = spyOn(stdout, 'write');
const spyStderr = spyOn(stderr, 'write');
await executeCommand(['list']);
expect(spyStdout).toHaveBeenCalled();
expect(spyStderr).not.toHaveBeenCalled();
Testing Exit Codes
import { mockProcess } from 'bun:test/mock';
mockProcess({
exit: (code: number) => {
exitCode = code;
},
});
await executeCommand(['invalid-command']);
expect(exitCode).toBe(1);
Testing Error Messages
it('should show error message on invalid input', async () => {
const spyConsoleError = spyOn(console, 'error');
await executeCommand(['create', 'Invalid_Name']);
expect(spyConsoleError).toHaveBeenCalledWith(
expect.stringContaining('Invalid skill name')
);
});
CI/CD Integration
GitHub Actions Test Workflow
# .github/workflows/test.yml
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
- name: Install dependencies
run: bun install
- name: Run tests
run: bun test --coverage
- name: Upload coverage
uses: codecov/codecov-action@v4
with:
files: ./coverage/coverage-final.json
Test Matrix Strategy
# Test across multiple Node versions and OS
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
bun-version: ['1.0.x', 'latest']
steps:
- name: Run tests
run: bun test
Quality Gates
Pre-commit Test Hooks
// package.json
{
"scripts": {
"test": "bun test",
"test:watch": "bun test --watch",
"test:coverage": "bun test --coverage",
"precommit": "bun test"
}
}
Linting Tests
# Run linter on test files
bun lint src/**/*.test.ts
# Format test files
bun format src/**/*.test.ts
Verification
After implementing test infrastructure:
- Test files organized alongside source code
- Coverage threshold (80%) is enforced
- Mocks are used for external dependencies
- CI/CD runs tests on every PR
- Output capture tests verify correct formatting
- Exit code tests validate error handling
- Pre-commit hooks prevent broken tests from committing
Examples from liaison-toolkit
Example 1: Testing Skill List Command
// packages/liaison/__tests__/skill.test.ts
import { describe, it, expect, mock } from 'bun:test';
describe('skill list command', () => {
it('should list all available skills', async () => {
// Mock discoverSkills
mock(() => ({
'.skills/library-research/SKILL.md': '...',
'.skills/git-automation/SKILL.md': '...',
}), async () => {
const result = await executeCommand(['skill', 'list']);
expect(result.skills.length).toBeGreaterThan(0);
});
});
});
Example 2: Testing Validation
import { describe, it, expect } from 'bun:test';
describe('skill validation', () => {
it('should reject invalid skill names', async () => {
const result = await validateSkillName('Invalid_Name');
expect(result.valid).toBe(false);
expect(result.errors[0].type).toBe('invalid-name');
});
it('should accept valid skill names', async () => {
const result = await validateSkillName('test-skill');
expect(result.valid).toBe(true);
});
});
Related Resources
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
security-scanning
Security scanning with dependency audits, secret detection, static analysis, and vulnerability management. Use when checking for vulnerabilities, scanning dependencies, or enforcing security gates.
bun-development
Bun build system, development workflow, and TypeScript patterns. Use when working with Bun projects, setting up development environment, or managing dependencies with Bun package manager.
library-research
Research library documentation and API references using Context7 integration. Use when users ask about libraries, frameworks, packages, APIs, or need documentation for third-party code.
release-publishing
Release workflow with changesets, version bumping, changelog generation, and npm publishing. Use when preparing releases, managing versions, or publishing to npm.
liaison-workflows
Task management and workflow patterns using liaison. Use when managing tasks, creating workflows, or working with task-driven automation.
cli-development
CLI development patterns with Commander.js, argument parsing, and user experience best practices. Use when creating commands, handling options, formatting output, or building CLI tools.
Didn't find tool you were looking for?