Use when writing tests, reviewing test code, or discussing testing approach for walkerOS packages. Covers env pattern, dev examples, and package-specific strategies.
Installation
Details
Usage
After installing, this skill will be available to your AI coding assistant.
Verify installation:
skills listSkill Instructions
name: testing-strategy description: Use when writing tests, reviewing test code, or discussing testing approach for walkerOS packages. Covers env pattern, dev examples, and package-specific strategies.
walkerOS Testing Strategy
Overview
walkerOS uses a layered testing approach with built-in patterns for mocking and documentation sync. This skill ensures tests are reliable, efficient, and maintainable.
Core principle: Test real behavior using the env pattern, link to dev
examples, verify before claiming complete.
The Rules
Rule 1: Use env for Mocking, Not Jest
walkerOS has a built-in dependency injection pattern via env in context. This
is lighter than Jest mocks, enables documentation generation, and keeps tests in
sync with examples.
Wrong:
jest.mock('../ga4', () => ({ initGA4: jest.fn() }));
expect(initGA4).toHaveBeenCalledWith(...);
Right:
import { examples } from '../dev';
import { mockEnv } from '@walkeros/core';
const calls: Array<{ path: string[]; args: unknown[] }> = [];
const testEnv = mockEnv(examples.env.push, (path, args) => {
calls.push({ path, args });
});
await destination.push(event, { ...context, env: testEnv });
expect(calls).toContainEqual({
path: ['window', 'gtag'],
args: ['event', 'page_view', { page_title: 'Home' }],
});
Rule 2: Link Tests to dev Examples
The dev.ts export provides examples.env, examples.events, and
examples.mapping. Using these in tests ensures documentation stays in sync.
import { examples } from '../dev';
// Use examples.env for mock environment
const testEnv = mockEnv(examples.env.push, interceptor);
// Assert against examples.events (documented expected output)
expect(calls[0].args).toEqual(examples.events.ga4PageView());
// Test with examples.mapping configurations
const config = { mapping: examples.mapping.ecommerce };
Rule 3: Test Real Behavior, Not Mock Behavior
If you're asserting that a mock was called, you're testing the mock works, not the code.
Red flags:
expect(mockFn).toHaveBeenCalled()without verifying the mock produces real effects- Assertions on
*-mocktest IDs - Tests that pass when mock is present, fail when removed
Fix: Test what the code actually does. If external APIs must be mocked, verify the real API would receive correct data.
Rule 4: Test First, Watch It Fail
If you didn't see the test fail, you don't know it tests the right thing.
Process:
- Write failing test
- Verify it fails for expected reason (missing feature, not typo)
- Write minimal code to pass
- Verify it passes
- Refactor if needed
Red flags:
- Test passes immediately when written
- Can't explain why test failed
- "I'll add tests later"
Rule 5: No Test-Only Methods in Production Code
Production classes shouldn't have methods only tests use.
Wrong:
class Session {
destroy() {
/* only used in tests */
}
}
Right:
// In test-utils/
export function cleanupSession(session: Session) { ... }
Rule 6: Verify Before Claiming Complete
"Should pass now" is not verification.
Process:
- Run the actual test command
- Read the output
- Confirm pass/fail count
- Only then claim status
When to Use Each Test Type
| Type | When to Add | Example |
|---|---|---|
| Integration | New usage pattern, new external API interaction, new data flow path | Collector → Destination → gtag() |
| Unit | Combinatorics, edge cases, pure function logic | Mapping variations, core utilities |
| Contract | Boundary validation | Destination output matches vendor API, source input validation |
Guideline: Integration tests prove things work when stuck together. Unit tests efficiently cover variations. Contract tests catch API drift.
Package-Specific Approaches
| Package | Approach |
|---|---|
| core | Unit tests only - pure functions, no env needed |
| collector | Integration tests critical - input/output consistency is paramount |
| browser source | Maintain walker algorithm coverage |
| web destinations | Integration tests per unique pattern + unit tests for mappings, use env pattern |
| server destinations | Same as web destinations |
| cli/docker | Integration tests for spawn behavior, explore dev pattern to reduce duplication |
| sources | Contract tests for input validation, integration tests for event capture |
The env Pattern Deep Dive
How env Works
Each destination/source defines an env type that specifies external
dependencies:
// Destination-specific env type
export interface Env extends DestinationWeb.Env {
window: {
gtag: Gtag.Gtag;
dataLayer: unknown[];
};
document: {
createElement: (tagName: string) => HTMLElement;
head: { appendChild: (node: unknown) => void };
};
}
mockEnv() Function
The mockEnv() function from @walkeros/core creates a Proxy that intercepts
all function calls:
import { mockEnv } from '@walkeros/core';
const calls: Array<{ path: string[]; args: unknown[] }> = [];
const testEnv = mockEnv(examples.env.push, (path, args) => {
calls.push({ path, args });
// Optionally return a value
});
// Now use testEnv in your destination context
await destination.push(event, { ...context, env: testEnv });
// Assert on captured calls
expect(calls).toContainEqual({
path: ['window', 'gtag'],
args: ['event', 'purchase', expect.objectContaining({ value: 99.99 })],
});
dev.ts Structure
Each package with external dependencies should have:
// src/dev.ts
export * as schemas from './schemas';
export * as examples from './examples';
// src/examples/index.ts
export * as env from './env';
export * as events from './events';
export * as mapping from './mapping';
Red Flags - Stop and Fix
- Using
jest.mock()for internal modules whenenvpattern is available - Tests that don't import from
../dev - Assertions only checking mock call counts
- Tests with extensive mock setup (>50% of test is setup)
- Test-only methods added to production classes
- Claiming tests pass without running them
Commands
# Run all tests
npm run test
# Run tests for specific package
cd packages/[name] && npm run test
# Run single test file
npm run test -- path/to/file.test.ts
# Watch mode
npm run test -- --watch
Related
- AGENT.md - Development guide
- .claude/skills/testing-strategy/ - Claude Code auto-discovery reference
More by elbwalker
View allUse when events aren't reaching destinations, debugging event flow, or troubleshooting mapping issues. Covers common problems and debugging strategies.
Use when configuring event mappings for specific use cases. Provides recipes for GA4, Meta, custom APIs, and common transformation patterns.
Use when writing or updating any documentation - README, website docs, or skills. Covers quality standards, example validation, and DRY patterns. (project)
Use when creating a new walkerOS source. Example-driven workflow starting with research and input examples before implementation.