Use this skill when writing, reviewing, or improving tests. Trigger on: 'write tests', 'add tests', 'test naming', 'what should I test', 'edge cases', 'test structure', 'assertion', 'toBe vs toEqual', 'toBeDefined', 'toBeTruthy', 'test review', 'test quality', 'test coverage', 'how to test', 'bug found write test', 'one test enough', 'test name', 'arrange act assert', 'AAA pattern'. Also trigger when user shares a test and asks for review, asks about assertion best practices, asks what edge cases to consider, mentions test names like 'test login' or 'should work', asks about splitting tests, or reports finding a bug and wants to know what else to test. Do NOT trigger for test runner configuration (jest.config, vitest.config), CI/CD pipeline setup, or questions about testing frameworks that aren't about how to write the tests themselves.
From hugin-v0npx claudepluginhub michelve/hugin-marketplace --plugin hugin-v0This skill uses the workspace's default tool permissions.
evals/evals.jsonevals/trigger-evals.jsonresources/assertion-examples.tsresources/test-structure-examples.tsskills-rules.jsonProvides checklists to review code for functionality, quality, security, performance, tests, and maintainability. Use for PRs, audits, team standards, and developer training.
Guides browser automation with Playwright, Puppeteer, Selenium for e2e testing and scraping. Teaches reliable selectors, auto-waits, isolation to fix flaky tests.
Enforces A/B test setup with gates for hypothesis locking, metrics definition, sample size calculation, assumptions checks, and execution readiness before implementation.
How to write tests that catch bugs, document behavior, and remain maintainable.
π¨ Test names describe outcomes, not actions. "returns empty array when input is null" not "test null input". The name IS the specification.
π¨ Assertions must match test titles. If the test claims to verify "different IDs", assert on the actual ID valuesβnot just count or existence.
π¨ Assert specific values, not types. expect(result).toEqual(['First.', ' Second.']) not expect(result).toBeDefined(). Specific assertions catch specific bugs.
π¨ One concept per test. Each test verifies one behavior. If you need "and" in your test name, split it.
π¨ Bugs cluster together. When you find one bug, test related scenarios. The same misunderstanding often causes multiple failures.
Pattern: [outcome] when [condition]
returns empty array when input is null
throws ValidationError when email format invalid
calculates tax correctly for tax-exempt items
preserves original order when duplicates removed
test null input // What about null input?
should work // What does "work" mean?
handles edge cases // Which edge cases?
email validation test // What's being validated?
Your test name should read like a specification. If someone reads ONLY the test names, they should understand the complete behavior of the system.
See resources/assertion-examples.ts for complete examples covering:
toBeDefined) vs strong (toEqual) assertionsSee resources/test-structure-examples.ts for complete examples covering:
When testing a function, systematically consider these edge cases based on input types.
""" "[], {}null inputundefined inputundefined vs missing keyThese test implicit assumptions in your domain:
When testing code that validates properties against type constraints (e.g., validating route: string in an interface):
Wrong-type literals:
route = 123)route = true)count = 'five')enabled = 'yes')Non-literal expressions:
route = `/path/${id}`)route = someVariable)route = getRoute())route = config.path)Correct type:
route = '/orders')'', zero 0, false)Why this matters: A common bug pattern is validating "is this a literal?" without checking "is this the RIGHT TYPE of literal?"
hasLiteralValue() returns true for 123, true, and 'string'hasStringLiteralValue() returns true only for 'string'When an interface specifies property: string, validation must reject numeric and boolean literals, not just non-literal expressions.
When you discover a bug, don't stopβexplore related scenarios:
If your test name says "test" or "should work": STOP. What outcome are you actually verifying? Name it specifically.
If you're asserting toBeDefined() or toBeTruthy(): STOP. What value do you actually expect? Assert that instead.
If your assertion doesn't match your test title: STOP. Either fix the assertion or rename the test. They must agree.
If you're testing multiple concepts in one test: STOP. Split it. Future you debugging a failure will thank you.
If you found a bug and wrote one test: STOP. Bugs cluster. What related scenarios might have the same problem?
If you're skipping edge cases because "that won't happen": STOP. It will happen. In production. At 3 AM.
When testing DSAI components, follow these additional patterns:
import { render, screen, fireEvent } from '@testing-library/react';
import { axe, toHaveNoViolations } from 'jest-axe';
import { Button } from '@/components/ui/button';
expect.extend(toHaveNoViolations);
describe('Button', () => {
it('renders with correct variant class', () => {
render(<Button variant="primary">Click me</Button>);
expect(screen.getByRole('button')).toHaveClass('btn', 'btn-primary');
});
it('shows spinner when loading', () => {
render(<Button loading>Save</Button>);
expect(screen.getByRole('status')).toBeInTheDocument();
expect(screen.getByRole('button')).toBeDisabled();
});
it('forwards ref to button element', () => {
const ref = { current: null };
render(<Button ref={ref}>Click</Button>);
expect(ref.current).toBeInstanceOf(HTMLButtonElement);
});
});
// Button.a11y.test.tsx
import { render } from '@testing-library/react';
import { axe, toHaveNoViolations } from 'jest-axe';
import { Button } from '@/components/ui/button';
expect.extend(toHaveNoViolations);
it('has no accessibility violations', async () => {
const { container } = render(<Button variant="primary">Click me</Button>);
expect(await axe(container)).toHaveNoViolations();
});
forwardRef β verify ref is forwarded to DOM elementdisplayName β verify component.displayName matches expected namecn() β verify className composition with custom classesvalue+onChange and defaultValue modes.a11y.test.tsx file with jest-axeit('forwards ref to the root DOM element', () => {
const ref = React.createRef<HTMLButtonElement>();
render(<Button ref={ref}>Click</Button>);
expect(ref.current).toBeInstanceOf(HTMLButtonElement);
expect(ref.current?.tagName).toBe('BUTTON');
});
it('has correct displayName', () => {
expect(Button.displayName).toBe('Button');
});
import { buttonFSMReducer, createInitialButtonFSMState } from './Button.fsm';
describe('Button FSM', () => {
it('transitions idle β hovered on HOVER', () => {
const initial = createInitialButtonFSMState({});
const next = buttonFSMReducer(initial, { type: 'HOVER' });
expect(next.status).toBe('hovered');
});
it('transitions hovered β pressed on PRESS', () => {
const hovered = buttonFSMReducer(
createInitialButtonFSMState({}),
{ type: 'HOVER' }
);
const pressed = buttonFSMReducer(hovered, { type: 'PRESS' });
expect(pressed.status).toBe('pressed');
});
it('stays disabled when receiving HOVER in disabled state', () => {
const disabled = buttonFSMReducer(
createInitialButtonFSMState({}),
{ type: 'DISABLE' }
);
const result = buttonFSMReducer(disabled, { type: 'HOVER' });
expect(result.status).toBe('disabled');
});
});
import { cn } from '@/utils/cn';
describe('cn()', () => {
it('joins truthy values', () => {
expect(cn('btn', 'btn-primary', 'btn-lg')).toBe('btn btn-primary btn-lg');
});
it('filters out falsy values', () => {
expect(cn('btn', false && 'btn-primary', undefined, null, '')).toBe('btn');
});
it('applies conditional classes', () => {
const variant = 'danger';
const size: string | undefined = undefined;
expect(cn('btn', `btn-${variant}`, size && `btn-${size}`)).toBe('btn btn-danger');
});
});
it('throws when sub-component used outside parent', () => {
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
expect(() => render(<Card.Header>Orphan</Card.Header>)).toThrow();
consoleSpy.mockRestore();
});
With TDD Process: This skill guides the RED phaseβhow to write the failing test well.
With Software Design Principles: Testable code follows design principles. Hard-to-test code often has design problems.