Tests refactor
This commit is contained in:
parent
245520fb50
commit
99134d8556
165 changed files with 911 additions and 531 deletions
|
|
@ -97,19 +97,20 @@ Separate each group with a blank line.
|
|||
| React hook | camelCase | `useProducts`, `usePantryItems` |
|
||||
| CSS class (Tailwind) | kebab-case | via Tailwind utilities |
|
||||
|
||||
## Implementation Workflow (Vertical Slice)
|
||||
## Implementation Workflow (TDD-First)
|
||||
|
||||
Every feature or phase implementation must follow this order to ensure consistency and type safety across the monorepo:
|
||||
Every feature or phase implementation must follow this exact test-first sequence, strictly practicing the Red-Green-Refactor cycle:
|
||||
|
||||
1. **Shared Layer (`packages/shared`)**: Define types, enums, and Zod schemas. Add unit tests.
|
||||
2. **Database Layer (`packages/api`)**: Create Mongoose schema and Repository. Use `.lean().exec()` on all reads. Add integration tests.
|
||||
3. **Service Layer (`packages/api`)**: Implement business logic using Awilix DI. Add unit tests.
|
||||
4. **Route Layer (`packages/api`)**: Create Fastify route plugin. Add route tests.
|
||||
5. **Web API Client (`packages/web`)**: Implement frontend service. Add unit tests.
|
||||
6. **Web UI (`packages/web`)**: Create Next.js pages/components (Server Components by default). Add component tests.
|
||||
1. **Shared Layer (`packages/shared`)**: Write validation schema and enum tests first in `tests/`. Implement schemas and types inside `src/`.
|
||||
2. **Database Layer (`packages/api`)**: Write schema and repository integration tests first in `tests/` (using `mongodb-memory-server`). Create Mongoose schema and Repository in `src/`. All reads must use `.lean().exec()`.
|
||||
3. **Service Layer (`packages/api`)**: Write service unit tests first in `tests/` using the `createMockRepository` helper to mock repository methods. Implement business logic Service class inside `src/` using Awilix constructor injection.
|
||||
4. **Route Layer (`packages/api`)**: Write Fastify route plugin tests first in `tests/` using `app.inject` and resolving mocks. Implement the Fastify route plugin inside `src/`.
|
||||
5. **Web API Client (`packages/web`)**: Write service unit tests first in `tests/` utilizing MSW to mock backend requests. Implement frontend service in `src/`.
|
||||
6. **Web UI (`packages/web`)**: Write component and page tests first in `tests/` using React Testing Library role queries. Create Next.js pages and components inside `src/` (Server Components by default).
|
||||
|
||||
**Mandatory Verification**: Every task must end with `npm run build`, `npm run test:cov`, and `npm run lint` all passing with the defined coverage thresholds.
|
||||
|
||||
|
||||
## Code Organization Rules
|
||||
|
||||
### API (NestJS)
|
||||
|
|
|
|||
167
docs/instructions/tdd.md
Normal file
167
docs/instructions/tdd.md
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
# Test-Driven Development (TDD) Guide — MeshiTrack
|
||||
|
||||
This guide serves as the definitive reference for writing, executing, and maintaining tests using a Test-Driven Development (TDD) methodology across all packages in the MeshiTrack monorepo.
|
||||
|
||||
---
|
||||
|
||||
## 1. Core Principles
|
||||
|
||||
TDD is a software development workflow where you **write tests first, before writing implementation code**. This ensures high reliability, enforces strict scoping of features, prevents untested paths, and results in cleaner designs.
|
||||
|
||||
### The Red-Green-Refactor Cycle
|
||||
|
||||
For every feature, schema, service, or route you build, follow this continuous loop:
|
||||
|
||||
1. **RED (Write a Failing Test)**:
|
||||
- Identify the small increment of behavior you want to add.
|
||||
- Write a test expressing that behavior.
|
||||
- Run the test and see it fail. **Compilation failures in TypeScript count as a valid "Red" state** for new types, interfaces, or class signatures. For logic, stub the signature and ensure it fails at runtime.
|
||||
2. **GREEN (Write Minimal Code)**:
|
||||
- Write the absolute minimum production code required to satisfy the test (even if it's returning hardcoded values temporarily).
|
||||
- Run the test and see it pass.
|
||||
3. **REFACTOR (Clean Up and Optimize)**:
|
||||
- Review your code (both production and test files). Remove duplication, improve readability, refine types, and optimize structure.
|
||||
- Run the test suite continuously to guarantee your refactoring did not break existing behavior (remains Green).
|
||||
|
||||
---
|
||||
|
||||
## 2. Directory Structure (Option B)
|
||||
|
||||
To keep production bundles completely clean, all test files and mock utilities live in a root-level `tests/` folder matching the structure of `src/`. **Never place tests inside `src/`.**
|
||||
|
||||
```
|
||||
packages/api/
|
||||
├── src/ # 100% Pure Production Code
|
||||
│ ├── modules/
|
||||
│ │ └── products/
|
||||
│ │ ├── products.service.ts
|
||||
│ │ └── products.repository.ts
|
||||
│ └── main.ts
|
||||
└── tests/ # 100% Test-First Files
|
||||
├── helpers/
|
||||
│ └── mock-repository.ts # Reusable mocking helpers
|
||||
├── modules/
|
||||
│ └── products/
|
||||
│ ├── products.service.test.ts
|
||||
│ └── products.repository.test.ts
|
||||
└── schemas/
|
||||
└── product.schema.test.ts
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Layer-by-Layer TDD Guide
|
||||
|
||||
### Layer 1: Shared Package (`packages/shared`)
|
||||
The shared package contains domain types, enums, and Zod schemas.
|
||||
|
||||
1. **Red**: Write a test in `packages/shared/tests/validation/feature.schemas.test.ts` asserting validation behaviors for correct, incorrect, and edge-case payloads.
|
||||
2. **Green**: Implement the validation schema and types in `packages/shared/src/validation/feature.schemas.ts`. Run `npm run test -w packages/shared` to verify it passes.
|
||||
3. **Refactor**: Clean up custom messages, dry up repeated Zod rules, and ensure exports are barrelled correctly in `index.ts`.
|
||||
|
||||
---
|
||||
|
||||
### Layer 2: Schema & Repository (`packages/api`)
|
||||
This layer handles persistence via Mongoose schemas and repositories.
|
||||
|
||||
1. **Red**: Write an integration test in `packages/api/tests/modules/<module>/<module>.repository.test.ts` using `mongodb-memory-server` asserting query conditions, indexes, and `householdId` filtering.
|
||||
2. **Green**: Create the Mongoose schema inside `src/schemas/` and the Repository class in `src/modules/<module>/`. All reads must use `.lean().exec()`. Verify tests pass.
|
||||
3. **Refactor**: Optimize indexes and verify schema options (e.g. `timestamps: true`).
|
||||
|
||||
---
|
||||
|
||||
### Layer 3: Service Layer (`packages/api`)
|
||||
Services contain core business logic, transactional bounds, and use constructor-injected dependencies via Awilix.
|
||||
|
||||
To eliminate boilerplate, import the `createMockRepository` helper from `tests/helpers/mock-repository.ts` to mock all repository methods automatically:
|
||||
|
||||
```typescript
|
||||
// packages/api/tests/modules/products/products.service.test.ts
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { ProductsService } from '../../../src/modules/products/products.service.js';
|
||||
import { ProductsRepository } from '../../../src/modules/products/products.repository.js';
|
||||
import { createMockRepository } from '../../helpers/mock-repository.js';
|
||||
|
||||
describe(ProductsService.name, () => {
|
||||
let service: ProductsService;
|
||||
let mockRepo: ReturnType<typeof createMockRepository<ProductsRepository>>;
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
mockRepo = createMockRepository(ProductsRepository);
|
||||
service = new ProductsService({ productsRepository: mockRepo as any });
|
||||
});
|
||||
|
||||
it('should find a product by household scope', async () => {
|
||||
// 1. RED: Write the test first, before service.findById exists
|
||||
mockRepo.findById.mockResolvedValue({ _id: '1', name: 'Apples' });
|
||||
const result = await service.getById('1', 'hh1');
|
||||
expect(result.name).toBe('Apples');
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
1. **Red**: Run the test. Verify it fails due to compilation (if the service/method is missing) or runtime failure (if stubbed).
|
||||
2. **Green**: Implement constructor injection and the method in `src/modules/<module>/<module>.service.ts`. Run the test to confirm it passes.
|
||||
3. **Refactor**: Extract logic, reduce cognitive complexity, and ensure proper typing.
|
||||
|
||||
---
|
||||
|
||||
### Layer 4: Route Layer (`packages/api`)
|
||||
Fastify routes define endpoints, validate payloads, and resolve services.
|
||||
|
||||
1. **Red**: Write a route test in `tests/modules/<module>/<module>.routes.test.ts` using Fastify's `app.inject()`. Mock the resolved service from the request DI scope. Assert status codes and payloads.
|
||||
2. **Green**: Register the route plugin in `src/modules/<module>/<module>.routes.ts`, wire validations via Zod type providers, and call the service. Verify it passes.
|
||||
3. **Refactor**: Clean up endpoint structures, route prefixes, and schema schemas.
|
||||
|
||||
---
|
||||
|
||||
### Layer 5: Web API Client (`packages/web`)
|
||||
Frontend fetch wrapper utilizing MSW to mock backend requests.
|
||||
|
||||
1. **Red**: Write a service test in `packages/web/tests/services/<service>.test.ts`. Configure MSW to mock the HTTP responses. Assert response modeling and mapping.
|
||||
2. **Green**: Implement the service wrapper using fetch in `packages/web/src/services/`. Verify it passes.
|
||||
3. **Refactor**: Clean up payload mappings and parameter serialization.
|
||||
|
||||
---
|
||||
|
||||
### Layer 6: Web UI Components (`packages/web`)
|
||||
React components and Next.js pages.
|
||||
|
||||
1. **Red**: Write a component test in `packages/web/tests/app/(dashboard)/<feature>/<Component>.test.tsx` using React Testing Library. Use accessible role queries (`screen.getByRole`) to assert loading/rendering states and user interactions.
|
||||
2. **Green**: Write the minimal TSX/JSX inside `packages/web/src/` to satisfy the tests.
|
||||
3. **Refactor**: Refine tailwind utility classes, modularize components, and check accessibility (ARIA tags).
|
||||
|
||||
---
|
||||
|
||||
## 4. Tooling & DX Guidelines
|
||||
|
||||
To make TDD fast and effective, use interactive watch-mode running continuously in the background. **Never run `npx`.**
|
||||
|
||||
### Continuous File-Specific Watch Mode (Fastest DX)
|
||||
To start watch mode for a specific test file during a Red-Green cycle:
|
||||
```bash
|
||||
# Watch API Service tests
|
||||
npm run test:watch -w packages/api -- tests/modules/products/products.service.test.ts
|
||||
|
||||
# Watch Web Component tests
|
||||
npm run test:watch -w packages/web -- tests/app/dashboard/ProductCard.test.tsx
|
||||
```
|
||||
|
||||
### Continuous Package-Wide Watch Mode
|
||||
To start watch mode for all tests in a package:
|
||||
```bash
|
||||
# Continuous API package tests
|
||||
npm run test:watch -w packages/api
|
||||
|
||||
# Continuous Web package tests
|
||||
npm run test:watch -w packages/web
|
||||
```
|
||||
|
||||
### Full-Suite Verification
|
||||
Before concluding any implementation phase, confirm that the entire verification gate is fully operational:
|
||||
```bash
|
||||
npm run build # Enforces typescript compile across all packages
|
||||
npm run test:cov # Verifies all tests pass and meet strict coverage gates
|
||||
npm run lint # Validates static conventions
|
||||
```
|
||||
Loading…
Add table
Add a link
Reference in a new issue