daramex-testing
DaraMex Testing Rules
Project-specific testing overlay for the DaraMex monorepo. Use alongside the generic tdd, vitest, and nestjs-best-practices skills — this skill provides the DaraMex-specific decisions layered on top.
API stack: NestJS + Jest 30 + ts-jest 29 + supertest (Clean/Hexagonal)
Panel stack: React + Vitest 4 + @testing-library/react 16 + jsdom
Mode: Strict TDD (RED → GREEN → TRIANGULATE) — see tdd skill
The 11 Non-Negotiable Rules
- A test that doesn't compile is not a test. It's a lie. When you refactor production code, you update the tests IN THE SAME PR. If you can't, DELETE the test — a deleted test is honest, a rotten test is false coverage.
- A bad test is worse than no test. It gives false confidence and slows refactors. Delete brittle tests — don't "fix" them.
- Test behavior, not implementation. If refactoring without changing behavior breaks the test, the test is wrong.
- Pyramid shape — many unit, some integration, few E2E. Never inverted.
- Every test name must describe a behavior or rule, not "should work" or "should create".
- AAA comments MANDATORY — every
itmust have literal// Arrange,// Act,// Assertcomments. Blank lines alone are NOT sufficient. This is a project-level convention, not a style choice. - Never use
as anyin mocks — always typed (jest.Mocked<IPort>orPartial<IPort>). - Verify arguments AND return values, not just "was called".
toHaveBeenCalled()alone is a code smell. - One logical assertion per test. If you need 2
describe.each, split into twoit. - No
new Date()orMath.random()unmocked — breaks FIRST-Repeatable. - Every bug fix follows the 4-step workflow: (1) write a test that reproduces the bug and FAILS, (2) apply the minimal fix, (3) confirm the test PASSES, (4) commit test + fix together in the same PR. Fixing before writing the test is REJECTED. See
patterns.mdsection 15 for the full workflow.
Testing Pyramid — what goes where
| Layer | Target | % of suite | Tool | What it verifies |
|---|---|---|---|---|
| Unit | Pure functions, domain entities, value objects, use case handlers (with mocked ports) | ~70% | Jest / Vitest | Business rules, edge cases, branches |
| Integration | Repositories against real DB, controllers via supertest, adapters against fake HTTP | ~20% | Jest + supertest / Vitest + MSW | Wiring, DB contracts, HTTP contracts |
| E2E | Complete user flows through the deployed app | ~10% | supertest (API) | Critical flows only (auth, checkout, onboarding) |
Anti-pattern: Ice cream cone 🍦 (few units, many E2E). Rejected.
Clean/Hexagonal layers — what to test in each
DaraMex API uses domain/ + application/ + infrastructure/ in every module. Each layer has a distinct testing approach:
🔹 Domain layer → PURE unit tests
- No NestJS, no mocks, no DB, no HTTP. Just plain TypeScript.
- Test: entities, value objects, domain services, domain events.
- Every branch, every
throw, every state transition gets a test. - Fastest and most valuable tests you'll write.
🔹 Application layer → unit tests with ports mocked
- Command handlers, query handlers, use cases.
- Dependencies (
IXxxRepository,IXxxGateway) are interfaces — mock withjest.Mocked<IXxx>. - Do NOT mock domain classes (they're pure, use them as-is).
- Verify:
Result.ok/Result.fail, arguments passed to ports, domain validations stop I/O early.
🔹 Infrastructure layer → integration tests
- Adapters (TypeORM repos, HTTP gateways, ElevenLabs/OpenAI clients, etc.).
- Controllers — use
@nestjs/testingTest.createTestingModule+supertest. - DB: prefer a test container or in-memory SQLite; assert real round-trips.
- External HTTP: mock with
nockor MSW, assert the adapter sends correct requests and parses responses correctly.
🔹 E2E → full flows
- Reserved for critical user journeys only.
- Boot the full app. Use real HTTP. Hit the real DB in a test schema.
- Do not duplicate CRUD integration tests here.
Panel (React) — Testing Library rules
- Query by role first (
getByRole('button', { name: /submit/i })), then by text, then by label. NEVER by class, id, or test-id unless unavoidable. userEventoverfireEvent— simulates real user interactions.- Do not test hooks directly unless the hook is the public API of a package. Test the component that uses the hook.
- Do not test
className, internal state, or DOM structure — test what the user sees and can do. - Assert accessible output:
screen.getByText,getByRole,findByLabelText. These double as a11y guarantees. - Every
onClick,onChange,onSubmitprop is a contract — test that it's called with correct args.
Anti-patterns to hunt down in this codebase
When auditing or refactoring DaraMex tests, flag and kill:
| Anti-pattern | How to spot it | Fix |
|---|---|---|
| Mock Hell | Only toHaveBeenCalled() asserts; tests pass with any behavior |
Assert arguments + return values |
| Existence tests | expect(method).toBeDefined() |
Delete — TypeScript already guarantees this |
| Constructor tests | expect(obj.name).toBe('x') right after new Obj({ name: 'x' }) |
Delete unless there's validation logic |
| CSS class assertions | toHaveClass('foo') |
Replace with role/text queries |
as any in mocks |
repo as any, service as any |
Replace with jest.Mocked<IPort> |
| Shared mutable state | Mocks defined at describe scope, reused across it without reset |
Move to beforeEach with fresh instances |
| Test the logger/metrics | expect(logger.log).toHaveBeenCalled() |
Delete — that's implementation detail |
new Date() without fixed arg |
Flaky timestamp comparisons | Use fixed dates or jest.useFakeTimers() |
| Unclear test names | "should work", "should create", "returns true" | Rewrite to describe the rule: "rejects creation when X is empty" |
Commands
# API (NestJS + Jest)
pnpm --filter api test # all tests
pnpm --filter api test -- path/to/file.spec.ts # single file
pnpm --filter api test -- --testPathPatterns="users" # by pattern
pnpm --filter api test:cov # with coverage
pnpm --filter api test:e2e # e2e only
pnpm --filter api exec npx jest --forceExit # when hangs
# Panel (React + Vitest)
pnpm --filter panel test # all tests
pnpm --filter panel test -- path/to/file.test.tsx # single file
pnpm --filter panel test -- --coverage # with coverage
pnpm --filter panel test -- --ui # Vitest UI
Resources
- Fundamentals →
references/fundamentals.md— Testing pyramid, FIRST principles, what to test vs what not to test. - Examples →
references/examples.md— Paired MAL/BIEN examples for every layer. Grows over time. - Real-world anti-patterns →
references/real-world-antipatterns.md— READ THIS. Specific anti-patterns measured in this codebase (as of 2026-04 audit): 186as anyin mocks, 192 Mock Hell lines, 129/187 suites broken by abandonment during refactors. Includes fix strategies prioritized by ROI. - Positive patterns →
references/patterns.md— The positive counterpart to anti-patterns. Covers AAA, fixture factories,jest.Mocked<IPort>,Task.create()vsTask.rehydrate(), passthrough mocks, invariant-based asserts,describe.eachusage, and test failure diagnosis (rotten vs broken vs unbuilt). - Tooling cheatsheet →
references/tooling-cheatsheet.md— Jest 30 + Vitest 4 APIs most developers underuse: parameterized tests (it.each,it.for), typed mocks, fake timers, partial matchers, module mocking, React Testing Library queries, NestJSTest.createTestingModule+ supertest. - Companion skills:
tdd— red-green-refactor loop (Strict TDD is mandatory in this repo)vitest— Vitest-specific patterns for the panelnestjs-best-practices— architectural rules that inform what to test
Quick Checklist Before Merging Tests
- The test compiles — running it doesn't produce
Test suite failed to runwith TS errors - Every
itname describes a rule or behavior, not "should work" - AAA comments present (
// Arrange,// Act,// Assert) — MANDATORY, not optional - No
as anyin mocks — all ports typed - Asserts verify arguments AND return values, not only
toHaveBeenCalled - Every error branch is tested (not just happy path) — with 4 layers: failure type, HTTP status, error code, short-circuit
-
resultfromhandler.execute()is always captured and asserted — neverawait handler.execute(...)with the result discarded - No test depends on another test's execution order
- No timestamp or randomness without a fixed seed/value
- Pyramid respected (don't add an E2E if an integration test would cover it)
- React tests use role/text queries, not CSS classes
- Bug fixes ship with a regression test
More from cmglezpdev/custom-skills
code-audit
>
9nestjs-wide-events
Implement structured, wide-event logging in NestJS applications following the canonical log line / wide event pattern. Use this skill whenever the user asks about logging, observability, debugging, or tracing in a NestJS app. Also trigger when the user mentions log lines, structured logging, canonical log lines, wide events, request context, observability, or asks how to improve their NestJS logging setup. Use this even if the user just says "add logging" to a NestJS project, since the wide event pattern should be the default, not scattered console.log calls.
9documentation
>
7nestjs-app-metrics
Add application-level Prometheus metrics to a NestJS app using the OpenTelemetry SDK and an OTel Collector. Covers HTTP RED metrics, Node.js runtime metrics, business metrics, and advanced observability patterns. Use this skill whenever the user wants to add metrics, dashboards, or monitoring to a NestJS application, mentions Prometheus, Grafana metrics, OpenTelemetry metrics, OTel Collector, or asks about SLIs/SLOs, or application-level monitoring in a NestJS context. Also trigger when the user wants custom counters, histograms, gauges, or summaries in NestJS. This skill focuses exclusively on metrics the APPLICATION must emit. It does not cover logging, tracing, infra-level metrics from cAdvisor, node-exporter, postgres-exporter, or redis-exporter.
1