Testing
Guide to running and writing tests for AgentVault.
Test Framework
AgentVault uses Vitest for testing.
Running Tests
All Tests
npm run test
Watch Mode
npm run test:watch
Specific Test File
npm run test tests/unit/format.test.ts
Filter Tests
# Run tests matching pattern
npm run test -- --grep "wallet"
Coverage
npm run test -- --coverage
Test Structure
tests/
├── unit/ # Unit tests
│ ├── format.test.ts
│ └── validation.test.ts
├── cli/ # CLI command tests
│ ├── init.test.ts
│ ├── deploy.test.ts
│ └── wallet.test.ts
├── deployment/ # Deployment tests
│ └── icpClient.test.ts
├── icp/ # ICP integration tests
│ └── actor.test.ts
└── integration/ # Full integration tests
└── workflow.test.ts
Test Statistics
AgentVault v1.0 includes 508 tests across 31 test files.
| Category | Tests | Files |
|---|---|---|
| Unit | ~150 | ~10 |
| CLI | ~200 | ~15 |
| Deployment | ~80 | ~3 |
| ICP | ~50 | ~2 |
| Integration | ~28 | ~1 |
Writing Tests
Basic Test
import { describe, it, expect } from 'vitest';
describe('MyModule', () => {
it('should do something', () => {
expect(myFunction()).toBe('expected');
});
});
Async Test
import { describe, it, expect } from 'vitest';
describe('AsyncModule', () => {
it('should handle async operations', async () => {
const result = await asyncFunction();
expect(result).toBeDefined();
});
});
Mocking
import { describe, it, expect, vi } from 'vitest';
describe('MockedModule', () => {
it('should use mocked function', () => {
const mock = vi.fn().mockReturnValue('mocked');
expect(mock()).toBe('mocked');
expect(mock).toHaveBeenCalled();
});
});
Setup and Teardown
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
describe('SetupModule', () => {
let resource: Resource;
beforeEach(() => {
resource = createResource();
});
afterEach(() => {
resource.cleanup();
});
it('should use resource', () => {
expect(resource.isReady()).toBe(true);
});
});
CLI Command Tests
Testing Commands
import { describe, it, expect } from 'vitest';
import { Command } from 'commander';
import { initCommand } from '../../cli/commands/init';
describe('init command', () => {
it('should have correct name', () => {
expect(initCommand.name()).toBe('init');
});
it('should have required options', () => {
const options = initCommand.options;
expect(options.some(o => o.long === '--force')).toBe(true);
});
});
Integration Tests
ICP Client Tests
import { describe, it, expect, beforeAll } from 'vitest';
import { ICPClient } from '../../src/deployment/icpClient';
describe('ICPClient', () => {
let client: ICPClient;
beforeAll(async () => {
client = new ICPClient({ network: 'local' });
});
it('should connect to local network', async () => {
const status = await client.getStatus();
expect(status).toBeDefined();
});
});
Test Configuration
vitest.config.ts
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
globals: true,
environment: 'node',
include: ['tests/**/*.test.ts'],
exclude: ['node_modules', 'dist'],
coverage: {
provider: 'v8',
reporter: ['text', 'json', 'html'],
},
},
});
Best Practices
Test Naming
// Good: Descriptive names
it('should throw error for invalid canister ID format', () => {
expect(() => validateCanisterId('invalid')).toThrow();
});
// Bad: Vague names
it('works', () => {
expect(fn()).toBe(true);
});
Arrange-Act-Assert
it('should calculate correct balance', () => {
// Arrange
const amount = 1000000000n;
const decimals = 9;
// Act
const result = formatBalance(amount, decimals);
// Assert
expect(result).toBe('1');
});
One Assertion Per Test (Generally)
// Good: Focused test
it('should return correct status', () => {
const agent = createAgent();
expect(agent.getStatus()).toBe('idle');
});
// Also acceptable: Related assertions
it('should initialize with default values', () => {
const agent = createAgent();
expect(agent.getStatus()).toBe('idle');
expect(agent.getTasks()).toEqual([]);
});
Avoid Magic Values
// Good: Named constants
const CANISTER_ID = 'abcde-aaaab';
const EXPECTED_BALANCE = '1.0';
it('should query balance', async () => {
const balance = await getBalance(CANISTER_ID);
expect(balance).toBe(EXPECTED_BALANCE);
});
Debugging Tests
Debug Single Test
# Run with Node debugger
node --inspect-brk node_modules/.bin/vitest run --no-threads
Console Output
it('should debug', () => {
console.log('Debug output:', result);
expect(result).toBeDefined();
});
Continuous Integration
Tests run automatically on:
- Pull requests
- Pushes to main branch
CI configuration:
# .github/workflows/test.yml
- name: Run tests
run: npm run test
Adding New Tests
- Create test file in appropriate
tests/subdirectory - Follow naming convention:
*.test.ts - Import from
vitest - Run tests to verify
# Create new test file
touch tests/unit/newFeature.test.ts
# Run new test
npm run test tests/unit/newFeature.test.ts