Skip to content

Commit 43ab030

Browse files
yjaaidicursoragent
andcommitted
docs: πŸ“ add 202-contract-testing instructions
Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 89cc189 commit 43ab030

1 file changed

Lines changed: 87 additions & 0 deletions

File tree

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
---
2+
sidebar_label: 202. Contract Testing
3+
---
4+
5+
# Contract Testing
6+
7+
## Setup
8+
9+
```sh
10+
pnpm cook start 202-contract-testing
11+
```
12+
13+
To make sure that the fake and the real implementation behave the same way, you will implement a **contract test**: a shared suite of expectations that will run against **every** implementation. In this project, `verifyRecipeRepositoryContract` is already wired up for:
14+
15+
- `RecipeRepositoryFake` in `recipe-repository.fake.spec.ts` (in-memory, fast)
16+
- the real HTTP `RecipeRepository` in `recipe-repository.wide.spec.ts` (slow, hits the network)
17+
18+
You must **implement the contract**.
19+
20+
## 🎯 Goal #1: `searchRecipes()` returns all recipes
21+
22+
When called with no filter, the repository should return recipes β€” including at least one named **Burger** (present in both the fake seed data and the remote API).
23+
24+
### πŸ“ Steps
25+
26+
#### 1. Run tests in watch mode.
27+
28+
```sh
29+
pnpm test
30+
```
31+
32+
#### 2. Go to `src/app/recipe/recipe-repository.contract.ts`.
33+
34+
#### 3. Implement the **returns all recipes** test.
35+
36+
- Create the recipe repository using the `createRecipeRepositoryFake` factory function.
37+
- Call `searchRecipes()` with no arguments.
38+
- Assert the result **contains** a recipe whose `name` is `Burger` (See [Partial object matching](#-partial-object-matching)).
39+
40+
#### 4. Turn on the test by replacing `it.todo` with `it`.
41+
42+
#### 5. Make sure the test passes for both the fake and the real implementation.
43+
44+
## 🎯 Goal #2: `searchRecipes` filters by keywords
45+
46+
Calling `searchRecipes({ keywords: 'burg' })` should return only recipes whose names match that filter β€” no unrelated recipes in the result.
47+
48+
### πŸ“ Steps
49+
50+
#### 1. Go to `src/app/recipe/recipe-repository.contract.ts`.
51+
52+
#### 2. Implement the **filters recipes containg "burg" keywords** test.
53+
54+
- Call `searchRecipes({ keywords: 'burg' })`.
55+
- Split results into recipes whose `name` includes `Burger` and recipes that do not.
56+
- Assert there is **at least one** burger recipe, and **no** other recipes.
57+
58+
#### 3. Replace `it.todo` with `it`.
59+
60+
#### 4. Make sure the test passes for both the fake and the real implementation.
61+
62+
## 🎯 Goal #3: No match returns an empty array
63+
64+
### πŸ“ Steps
65+
66+
#### 1. Go to `src/app/recipe/recipe-repository.contract.ts`.
67+
68+
#### 2. Implement the **returns an empty array when no recipes are found** test.
69+
70+
- Call `searchRecipes` with keywords that cannot match any seeded or API recipe (e.g. `pizza with salmon and pineapple`).
71+
- Assert the result has length `0`.
72+
73+
#### 3. Replace `it.todo` with `it`.
74+
75+
#### 4. Make sure the test passes for both the fake and the real implementation.
76+
77+
## πŸ“– Appendices
78+
79+
### 🎁 Partial object matching
80+
81+
Use `toContainEqual` with `expect.objectContaining` when you only care about a subset of fields:
82+
83+
```ts
84+
expect(recipes).toContainEqual(expect.objectContaining({ name: 'Burger' }));
85+
```
86+
87+
This avoids brittle full-object equality when recipes carry ids, ingredients, etc.

0 commit comments

Comments
Β (0)