Rules Hub
Coding Rules Library
Rule priority, scope & exceptions
Use this to align rules with the senior-level structure (P0/P1/P2, scope, exceptions/tradeoffs).
Name fluent query-builder test mocks after the terminal method
Build query-chain mocks (Drizzle/Knex) with intermediate calls returning `this` and the resolving method named in the factory (makeOrderByDb / makeWhereDb).
Bad example
| 1 | // one opaque mock per test, unclear where the chain ends |
| 2 | const db = { |
| 3 | select: () => ({ |
| 4 | from: () => ({ |
| 5 | where: () => ({ orderBy: () => Promise.resolve([row]) }), |
| 6 | }), |
| 7 | }), |
| 8 | }; |
| 9 | // duplicated and re-typed in every test; you can't tell which call resolves |
Explanation (EN)
Hand-rolled nested mocks duplicate the chain in every test and hide which method actually resolves, so tests are noisy and brittle to chain changes.
Objašnjenje (HR)
Rucno pisani ugnijezdjeni mockovi dupliciraju lanac u svakom testu i skrivaju koja metoda zapravo razrjesava, pa su testovi bucni i lomljivi na promjene lanca.
Good example
| 1 | /** DB chain where orderBy is the terminal */ |
| 2 | function makeOrderByDb(terminal: unknown[] = []) { |
| 3 | const orderBy = jest.fn().mockResolvedValue(terminal); |
| 4 | const chain = { from: jest.fn().mockReturnThis(), where: jest.fn().mockReturnThis(), orderBy }; |
| 5 | return { db: { select: jest.fn().mockReturnValue(chain) }, orderBy }; |
| 6 | } |
| 7 | // test: const { db } = makeOrderByDb([row]); -> intent is in the factory name |
Explanation (EN)
Naming the factory after the terminal method documents the chain shape, returns the spy on the resolving call for assertions, and is reused across cases with one line.
Objašnjenje (HR)
Imenovanje tvornice po zavrsnoj metodi dokumentira oblik lanca, vraca spy na razrjesavajucem pozivu za provjere i ponovno se koristi u vise slucajeva jednom linijom.