Skip to content

Commit b2e5e11

Browse files
[mabel] Add more exercises and edits to Express.js lessons
1 parent 1e41cda commit b2e5e11

File tree

5 files changed

+208
-14
lines changed

5 files changed

+208
-14
lines changed

docs/backend-web-development/express-param-processing.md

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,4 @@ To define the parameter callbacks on a router instead, use `router.param`.
6969
## Exercises
7070
7171
Refactor the previous songs route to use `app.param` for the `id` parameter.
72-
Find the song of that particular id and put the song in the request object.
73-
74-
```js
75-
req.song = song;
76-
```
72+
Find the song of that particular id and put the song in the request object as a property of the request object.

docs/backend-web-development/express-routers.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,3 +197,10 @@ Now we can support both API versions!
197197

198198
What do you see when you go to http://localhost:3000/v1 on the browser?
199199
How about http://localhost:3000/v2 ?
200+
201+
## Exercises
202+
203+
Now that we have both movies and songs routes on the same App.js file, we should split them into a movies router and songs router.
204+
205+
Make sure that the tests are passing as you refactor the routes.
206+
After refactoring the code to their individual routers, we shall refactor the tests to their individual test files too.

docs/backend-web-development/express-simple-server.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,10 @@ app.delete("/", (req, res) => {
5050

5151
### cURL
5252

53-
Client URL
54-
cURL is by default already installed in Mac OS. For Windows, you can download cURL separately.
55-
You can send different HTTP requests.
53+
cURL stands for Client URL.
54+
cURL is by default already installed in Mac OS. For Windows, if you have anything older than Windows 10 build 1706, you can download cURL separately. Else, you might already have cURL.
55+
56+
You can send different HTTP requests using cURL.
5657

5758
For example, let's send a POST request to https://localhost:3000/ with a JSON message body.
5859

docs/backend-web-development/express-testing.md

Lines changed: 180 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,78 @@ describe("POST /", () => {
234234
});
235235
```
236236

237+
### Writing test for routes
238+
239+
Let's see an example of writing an actual test for a route.
240+
241+
We want to make a test for the following endpoint:
242+
243+
POST /songs route of the songs API
244+
245+
Test: POST /songs should return a new song object
246+
Route: POST /songs
247+
Expected response status code: 201
248+
Example request body:
249+
250+
```json
251+
{ "name": "test movie", "artist": "rhianna" }
252+
```
253+
254+
Example JSON response:
255+
256+
```json
257+
{ "id": 1, "name": "test movie", "artist": "rhianna" }
258+
```
259+
260+
```js
261+
it("POST /songs should add a song and return a new song object", async () => {
262+
const newSong = { name: "test movie", artist: "rhianna" };
263+
const expectedSong = { id: 1, name: "test movie", artist: "rhianna" };
264+
265+
const { response } = await request(app)
266+
.post("/songs")
267+
.send(newSong)
268+
.expect(201);
269+
270+
expect(response.status).toEqual(201);
271+
expect(response.body).toEqual(expectedSong);
272+
});
273+
```
274+
275+
Could also be written as:
276+
277+
```js
278+
it("POST /songs should add a song and return a new song object", async () => {
279+
const newSong = { name: "test movie", artist: "rhianna" };
280+
const expectedSong = { id: 1, name: "test movie", artist: "rhianna" };
281+
282+
const { body: actualSong } = await request(app)
283+
.post("/songs")
284+
.send(newSong)
285+
.expect(201);
286+
287+
expect(actualSong).toEqual(expectedSong);
288+
});
289+
```
290+
291+
### toMatchObject
292+
293+
Sometimes, we do not want to test for an exact match for the response object. We are satisfied with having certain object properties. We can make use of a useful Jest method called `toMatchObject`.
294+
295+
One possible use of this is for the GET /songs/:id route.
296+
297+
```js
298+
it("GET /songs/:id should return the correct song", () => {
299+
const expectedSong = {name: "test song", artist: "rhianna"};
300+
301+
const {body: actualSong} = await request(app)
302+
.get("/songs/1")
303+
.expect(200)
304+
305+
expect(actualSong).toMatchObject(expectedSong);
306+
});
307+
```
308+
237309
### Accessing agent in SuperTest
238310

239311
```js
@@ -262,6 +334,112 @@ There are some cases where we need to reuse the agent again. For example, we mig
262334

263335
Check the [github page for supertest](https://github.com/visionmedia/supertest) for an example on how to access the agent in SuperTest and reuse the agent to persist a request and its cookies.
264336

265-
## Exercises
337+
## Exercises
338+
339+
Add tests to the existing songs API we have been building.
340+
341+
## TDD with Express.js
266342

267-
Add tests to the songs API we have been building.
343+
Besides songs and the music industry, now our company would like to go into the movie industry. We need more routes on our API. They will now return movie information.
344+
345+
Let's try TDD for movie routes.
346+
347+
Add the tests for the movies endpoints.
348+
349+
Test: POST /movies should return a new movie object
350+
Route: POST /movies
351+
Expected response status code: 201
352+
Example request body:
353+
354+
```json
355+
{
356+
"movieName": "Lion King"
357+
}
358+
```
359+
360+
Example JSON response:
361+
362+
```json
363+
{
364+
"id": 1,
365+
"movieName": "Lion King"
366+
}
367+
```
368+
369+
Test: GET /songs should return an array containing one song
370+
Route: GET /movies
371+
Expected response status code: 200
372+
Example JSON response:
373+
374+
```json
375+
[
376+
{
377+
"id": 1,
378+
"movieName": "Lion King"
379+
}
380+
]
381+
```
382+
383+
Test: GET /movies/:id should return the movie with id
384+
Route: GET /movies/1
385+
Expected response status code: 200
386+
Example JSON response:
387+
388+
```json
389+
{
390+
"id": 1,
391+
"movieName": "Lion King"
392+
}
393+
```
394+
395+
Test: PUT /movies/:id should return the updated movie
396+
Route: PUT /movies/1
397+
Expected response status code: 200
398+
399+
Example request body:
400+
401+
```json
402+
{
403+
"movieName": "Frozen 2"
404+
}
405+
```
406+
407+
Example JSON response:
408+
409+
```json
410+
{
411+
"id": 1,
412+
"movieName": "Frozen 2"
413+
}
414+
```
415+
416+
Test: DELETE /movies/:id should return the deleted movie
417+
Route: DELETE /movies/1
418+
Expected response status code: 200
419+
420+
Example request body:
421+
422+
```json
423+
{
424+
"movieName": "Frozen 2"
425+
}
426+
```
427+
428+
Example JSON response:
429+
430+
```json
431+
{
432+
"id": 1,
433+
"movieName": "Frozen 2"
434+
}
435+
```
436+
437+
Test: GET /movies should return an empty array
438+
Route: GET /movies
439+
Expected response status code: 200
440+
441+
Example JSON response:
442+
443+
```json
444+
[]
445+
```

docs/backend-web-development/mongoose-middleware.md

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,24 @@ For instance, models have `pre` and `post` functions that take two parameters:
1414

1515
https://www.freecodecamp.org/news/introduction-to-mongoose-for-mongodb-d2a7aa593c57/
1616

17+
## Hashing password with bcrypt
18+
1719
Hashing user's password with bcrypt before saving to database.
1820

1921
```js
20-
userSchema.pre("save", async function(next) {
21-
const rounds = 10;
22-
this.password = await bcrypt.hash(this.password, rounds);
23-
next();
22+
userSchema.pre("save", async function (next) {
23+
if (this.isModified("password")) {
24+
const rounds = 10;
25+
this.password = await bcrypt.hash(this.password, rounds);
26+
next();
27+
}
2428
});
2529
```
30+
31+
https://mongoosejs.com/docs/api.html#document_Document-isModified
32+
33+
Why should we check for `isModified`?
34+
35+
`isModified` will only return true if you are changing the password or setting it for the first time.
36+
37+
It will not be triggered, for example, if the user's name is changed.

0 commit comments

Comments
 (0)