Spaces:
Sleeping
Sleeping
File size: 5,017 Bytes
b6e19c7 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 | # Testing β Strategy, Patterns & CLI Commands
## Purpose
Read this file when writing tests or running them.
Follow this exactly for every feature implementation.
---
## Core Principle
**Write tests after every feature. Never at the end of the project.**
Every test failure must tell you:
- WHICH test failed (test name + file)
- WHY it failed (expected vs received)
- WHERE it failed (line number)
---
## Testing Stack
- **Unit + Integration:** Jest + ts-jest
- **API Routes:** Supertest
- **UI Components:** React Testing Library
- **DB (Supabase):** Mocked with jest.mock or Supabase local instance
---
## Folder Structure
tests/
βββ unit/
β βββ lib/
β βββ utils/
βββ integration/
β βββ api/
β βββ db/
βββ components/
βββ setup.ts
---
## Test File Naming Convention
[feature-name].test.ts β unit test
[feature-name].integration.test.ts β integration test
[component-name].test.tsx β component test
---
## Standard Test Template
```typescript
// tests/unit/lib/fetchUser.test.ts
import { fetchUser } from '@/lib/supabase'
describe('fetchUser', () => {
it('returns user when valid userId is provided', async () => {
const result = await fetchUser('valid-uuid')
expect(result).not.toBeNull()
expect(result).toHaveProperty('id')
})
it('returns null when userId is null', async () => {
const result = await fetchUser(null as any)
expect(result).toBeNull()
})
it('returns null when userId is empty string', async () => {
const result = await fetchUser('')
expect(result).toBeNull()
})
it('handles DB error gracefully', async () => {
// mock supabase to throw
jest.spyOn(supabase, 'from').mockImplementationOnce(() => {
throw new Error('DB connection failed')
})
const result = await fetchUser('valid-uuid')
expect(result).toBeNull()
})
})
```
---
## Edge Cases to Test for Every Feature
| Scenario | What to Test |
|----------|-------------|
| Empty input | null, undefined, empty string |
| Auth state | unauthenticated user, expired token |
| DB response | empty array, null, malformed data |
| Network | timeout, connection failure |
| Duplicates | calling same function twice simultaneously |
| Boundary | max length strings, zero values, negative numbers |
---
## CLI Commands
### Run all tests
```bash
npm test 2>&1 | tee logs/test.log
```
### Run a specific test file
```bash
npm test -- tests/unit/lib/fetchUser.test.ts 2>&1 | tee logs/test.log
```
### Run tests in watch mode
```bash
npm test -- --watch
```
### Run tests with coverage
```bash
npm test -- --coverage 2>&1 | tee logs/test.log
```
### Run only failed tests
```bash
npm test -- --onlyFailures 2>&1 | tee logs/test.log
```
---
## Test Log Format
All test output pipes to `logs/test.log`.
When a test fails, the log will show:
FAIL tests/unit/lib/fetchUser.test.ts
β fetchUser βΊ returns null when userId is null
expect(received).toBeNull()
Received: { id: 'abc', name: 'test' }
14 | it('returns null when userId is null', async () => {
15 | const result = await fetchUser(null as any)
> 16 | expect(result).toBeNull()
| ^
17 | })
at Object.<anonymous> (tests/unit/lib/fetchUser.test.ts:16:20)
---
## scripts/test.sh
```bash
#!/bin/bash
echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] Starting test run..." >> logs/test.log
npm test 2>&1 | tee -a logs/test.log
EXIT_CODE=${PIPESTATUS[0]}
if [ $EXIT_CODE -ne 0 ]; then
echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] TESTS FAILED β see above for details" >> logs/test.log
else
echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] ALL TESTS PASSED" >> logs/test.log
fi
exit $EXIT_CODE
```
---
## Test Update Rules
- [ ] Write tests immediately after each feature is implemented
- [ ] Never delete existing tests β only add or update
- [ ] If a test is skipped, add a comment explaining why
- [ ] All tests must pass before marking a phase complete
- [ ] Run full test suite before every deployment
---
## Mocking Supabase
```typescript
// tests/setup.ts
jest.mock('@/lib/supabase', () => ({
supabase: {
from: jest.fn().mockReturnValue({
select: jest.fn().mockReturnValue({
eq: jest.fn().mockResolvedValue({ data: [], error: null })
}),
insert: jest.fn().mockResolvedValue({ data: null, error: null }),
update: jest.fn().mockResolvedValue({ data: null, error: null }),
delete: jest.fn().mockResolvedValue({ data: null, error: null })
}),
auth: {
getSession: jest.fn().mockResolvedValue({ data: { session: null }, error: null }),
refreshSession: jest.fn().mockResolvedValue({ data: null, error: null })
}
}
}))
```
---
## What NOT to Do
- Do not write tests after the entire project is done
- Do not mock everything β integration tests must hit real logic
- Do not skip edge case tests to save time
- Do not ignore a failing test β fix it before moving on
- Do not write tests that always pass regardless of logic |