Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Testing workflow and quality standards for writing and running tests. Use when: (1) Writing new tests, (2) Adding a new feature that needs tests, (3) Modifying logic that has existing tests, (4) Before claiming a task is complete.
.claude/skills/iofficeai-testing/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-07 | ✗→✓ | ▲ Improved | 29% | 0% |
| case-08 | ✗→✓ | ▲ Improved | -22% | 0% |
| case-10 | ✗→✓ | ▲ Improved | -41% | 0% |
| case-11 | ✗→✓ | ▲ Improved | 18% | 0% |
| case-12 | ✗→✓ | ▲ Improved | 105% | 0% |
Standards and workflow for writing and running tests. Every feature must be tested.
Announce at start: "I'm using testing skill to ensure proper test coverage."
Vitest 4 — configured in vitest.config.ts.
tests/
├── unit/ # Individual functions, utilities, components
├── integration/ # IPC, database, service interactions
├── regression/ # Regression test cases
└── e2e/ # End-to-end tests (Playwright, playwright.config.ts)| Environment | When | File naming | | ---------------- | --------------------------------- | --------------- | | node (default) | Main process, utilities, services | *.test.ts | | jsdom | DOM/browser-dependent code | *.dom.test.ts |
Before writing tests, list the riskiest scenarios first:
undefined / throws?Follow these quality rules:
1. Describe behavior, not code structure
typescript// Wrong — describes implementation it('should call repo.getConversation', ...) // Correct — describes behavior it('should return cached task without hitting repo on second call', ...) it('should reject with error when conversation does not exist', ...)
2. Every describe block must cover at least one failure path
Happy-path-only tests leave the most dangerous code untested.
3. One behavior per test
Keep each it() focused. More than 3 expect() calls in one test is a signal it is testing too much at once.
4. Self-check
After writing a test, mentally delete the core logic it targets. If the test would still pass, rewrite it — it is not guarding anything.
5. Start from risk, not from coverage gaps
List scenarios most likely to produce bugs. Write those first. Coverage is the outcome, not the starting point.
bashbun run test # Run all tests (REQUIRED before every commit) bun run test:coverage # Check coverage (before opening a PR)
Coverage target: ≥ 80% for all source files matched by vitest.config.ts → coverage.include (currently src/**/*.{ts,tsx} plus a few scripts).
New source files are automatically included in coverage — no manual config changes needed. If a new file is accidentally excluded by a rule in coverage.exclude, remove it from the exclude list.
When modifying logic, check if existing tests need updating:
bashbun run test -- --reporter=verbose # See which tests pass/fail with names
If a test fails because the behavior changed intentionally, update the test. If it fails unexpectedly, investigate.
When testing a module, verify:
null / undefined inputs handledBefore submitting code:
bun run test passescoverage.excludebun run test:coverage meets ≥ 80% target| Mistake | Correct | | --------------------------------- | --------------------------------------------- | | Testing implementation details | Test observable behavior | | Only testing happy path | Must include at least one failure path | | 5+ expects in one it() | Split into separate tests | | Skipping tests for "simple" code | Simple code breaks too — test the risky parts | | Writing tests after saying "done" | Tests are part of "done", not an afterthought |
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→fail | 10,592 | 2,177 | -79% | 1 | 1 | 0% | 2,169 | 1,330 | -39% | 0 | 0 | — |
case-02 | fail→fail | 44,627 | 2,347 | -95% | 1 | 1 | 0% | 1,227 | 1,323 | +8% | 0 | 0 | — |
case-03 | fail→fail | 89,656 | 2,002 | -98% | 1 | 1 | 0% | 6,188 | 1,235 | -80% | 0 | 0 | — |
case-04 | pass→fail | 16,955 | 25,178 | +48% | 1 | 1 | 0% | 3,609 | 6,477 | +79% | 0 | 0 | — |
case-05 | pass→fail | 10,630 | 2,063 | -81% | 1 | 1 | 0% | 2,092 | 1,311 | -37% | 0 | 0 | — |
case-06 | pass→fail | 10,825 | 11,356 | +5% | 1 | 1 | 0% | 2,166 | 3,441 | +59% | 0 | 0 | — |
case-07 | fail→pass | 5,785 | 1,561 | -73% | 1 | 1 | 0% | 1,017 | 1,308 | +29% | 0 | 0 | — |
case-08 | fail→pass | 71,707 | 2,889 | -96% | 1 | 1 | 0% | 1,916 | 1,499 | -22% | 0 | 0 | — |
case-09 | pass→pass | 10,395 | 1,695 | -84% | 1 | 1 | 0% | 1,591 | 1,291 | -19% | 0 | 0 | — |
case-10 | fail→pass | 13,614 | 2,627 | -81% | 1 | 1 | 0% | 2,302 | 1,365 | -41% | 0 | 0 | — |
case-11 | fail→pass | 13,366 | 5,213 | -61% | 1 | 1 | 0% | 1,540 | 1,815 | +18% | 0 | 0 | — |
case-12 | fail→pass | 6,700 | 6,005 | -10% | 1 | 1 | 0% | 970 | 1,984 | +105% | 0 | 0 | — |
case-13 | pass→pass | 11,317 | 6,372 | -44% | 1 | 1 | 0% | 1,940 | 2,198 | +13% | 0 | 0 | — |
case-14 | fail→pass | 6,220 | 1,507 | -76% | 1 | 1 | 0% | 1,157 | 1,288 | +11% | 0 | 0 | — |
case-15 | pass→pass | 18,144 | 12,151 | -33% | 1 | 1 | 0% | 2,922 | 3,107 | +6% | 0 | 0 | — |
case-16 | fail→pass | 8,485 | 1,197 | -86% | 1 | 1 | 0% | 1,394 | 1,207 | -13% | 0 | 0 | — |
case-17 | fail→pass | 3,830 | 1,813 | -53% | 1 | 1 | 0% | 471 | 1,256 | +167% | 0 | 0 | — |
case-18 | fail→pass | 24,768 | 1,424 | -94% | 1 | 1 | 0% | 1,434 | 1,257 | -12% | 0 | 0 | — |
case-19 | pass→pass | 9,490 | 7,159 | -25% | 1 | 1 | 0% | 1,791 | 2,244 | +25% | 0 | 0 | — |
case-20 | pass→pass | 11,026 | 4,181 | -62% | 1 | 1 | 0% | 1,906 | 1,693 | -11% | 0 | 0 | — |
case-21 | pass→pass | 6,730 | 2,821 | -58% | 1 | 1 | 0% | 1,205 | 1,501 | +25% | 0 | 0 | — |
case-22 | fail→pass | 12,260 | 2,114 | -83% | 1 | 1 | 0% | 1,828 | 1,351 | -26% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +32 percentage points is the difference between those two pass rates over the 22 comparable cases. 3 cases got worse with the skill loaded, and they are included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.