Skip to content

Commit ccfb9f6

Browse files
committed
review improvements (Jan)
1 parent f03e431 commit ccfb9f6

1 file changed

Lines changed: 15 additions & 8 deletions

File tree

.agents/skills/write-tests/SKILL.md

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ it('handles the request', async () => {
102102
// Good: asserts on the observable outcome
103103
it('sets transaction name from route path', () => {
104104
responseHandler(createMockContext(200));
105+
105106
expect(mockSetTransactionName).toHaveBeenCalledWith('GET /test');
106107
});
107108
```
@@ -111,9 +112,9 @@ it('sets transaction name from route path', () => {
111112
Default to exact matching. `toMatchObject`, `expect.objectContaining`, and `expect.arrayContaining`
112113
silently ignore fields that matter. This has caused real bugs to ship in this codebase.
113114

114-
**Use `toEqual` unless you have a specific reason not to.** The same applies to
115-
`toHaveBeenCalledWith` — spell out every argument rather than wrapping in `objectContaining`.
116-
This is the single most common place where loose assertions creep in:
115+
**Use `toEqual` by default.** The same applies to `toHaveBeenCalledWith` — spell out every
116+
argument rather than wrapping in `objectContaining`. This is the single most common place where
117+
loose assertions creep in:
117118

118119
```typescript
119120
// Bad: silently ignores any missing or extra properties in the call
@@ -129,8 +130,13 @@ expect(startSpan).toHaveBeenCalledWith({
129130
});
130131
```
131132

132-
When you genuinely can't enumerate all fields (e.g., a large framework-generated object), fall
133-
back to individual `.toBe()` checks on the fields that matter:
133+
The only valid reasons to use `toMatchObject` or `objectContaining` are: **(1)** the object is
134+
generated by a framework or third-party library and contains fields you don't control (timestamps,
135+
random IDs, internal framework state), or **(2)** the object has 10+ fields and the test only
136+
cares about 2–3 of them (in which case individual `.toBe()` checks on those fields are still
137+
preferred). If you wrote the object being asserted, you can spell it out — use `toEqual`.
138+
139+
When you do fall back, prefer individual `.toBe()` checks over `objectContaining`:
134140

135141
```typescript
136142
expect(event.transaction).toBe('GET /users/:id');
@@ -183,7 +189,7 @@ const url = 'https://api.example.com/users/42?include=profile&format=json';
183189

184190
### Boundary Value Analysis
185191

186-
If the valid range is 1–100, test 0, 1, 2, 99, 100, 101. Bugs cluster at boundaries — off-by-one
192+
If the valid range is 1–100, test -2, -1, 0, 1, 2, 99, 100, 101, Number.POSITIVE_INFINITY. Bugs cluster at boundaries — off-by-one
187193
errors, inclusive/exclusive confusion, type coercion.
188194

189195
### Test the unhappy path as hard as the happy path
@@ -203,7 +209,8 @@ Each edge case gets its own test with a descriptive name.
203209

204210
- Name test files `*.test.ts`, mirroring the source path: `src/shared/patchRoute.ts`
205211
`test/shared/patchRoute.test.ts`.
206-
- Import from the source path (`../../src/...`), not from the package's published API.
212+
- Import the module under test from its source path (`../../src/...`). But when importing from a _different_ package
213+
(e.g., `@sentry/core` in a `@sentry/node` test), use the package name — that's a real dependency, not the code under test.
207214
- For browser-environment tests: `/** @vitest-environment jsdom */` at top of file.
208215

209216
### Mocking
@@ -279,7 +286,7 @@ beforeEach(() => {
279286

280287
### Grouping
281288

282-
Two levels of `describe` is usually enough. Deeper nesting makes tests harder to find and read.
289+
1-2 levels of `describe` is usually enough. Deeper nesting makes tests harder to find and read.
283290

284291
```typescript
285292
describe('patchRoute', () => {

0 commit comments

Comments
 (0)