©  Caio Ribeiro Pereira 2016

Caio Ribeiro Pereira, Building APIs with Node.js , 10.1007/978-1-4842-2442-7_9

9. Testing the Application: Part 2

Caio Ribeiro Pereira

(1)São Vicente - SP, São Paulo, Brazil

Continuing the test implementation, now focus next on writing some tests for the resources: tasks and users.

Testing a Task’s Endpoints

To test the endpoints of a task’s resource, we are going to cheat the JWT authentication . After all, it will be necessary to correctly test the results of this resource and also the other resources that involves user authentication. To start, let’s create the structure for the tasks test.

Create the file test/routes/tasks.js with the following code.

 1   import jwt from "jwt-simple";
 2   
 3   describe("Routes: Tasks", () => {
 4     const Users = app.db.models.Users;
 5     const Tasks = app.db.models.Tasks;
 6     const jwtSecret = app.libs.config.jwtSecret;
 7     let token;
 8     let fakeTask;
 9     beforeEach(done => {
10       // Runs before each test...
11     });
12     describe("GET /tasks", () => {
13       describe("status 200", () => {
14         it("returns a list of tasks", done => {
15           // Test's logic...
16         });
17       });
18     });
19     describe("POST /tasks/", () => {
20       describe("status 200", () => {
21         it("creates a new task", done => {
22           // Test's logic...
23         });
24       });
25     });
26     describe("GET /tasks/:id", () => {
27       describe("status 200", () => {
28         it("returns one task", done => {
29           // Test's logic...
30         });
31       });
32       describe("status 404", () => {
33         it("throws error when task not exist", done => {
34           // Test's logic...
35         });
36       });
37     });
38     describe ("PUT /tasks/:id", () => {
39       describe("status 204", () => {
40         it("updates a task", done => {
41           // Test's logic...
42         });
43       });
44     });
45     describe("DELETE /tasks/:id", () => {
46       describe("status 204", () => {
47         it("removes a task", done => {
48           // Test's logic...
49         })
50       });
51     });
52   });

Detailing how to cheat the authentication part, we are going to reuse the module jwt-simple to create a valid token that will be used in the header of all the tests. This token will be repeatedly generated within the callback of the function beforeEach(done). To generate it, though, we have to delete all users first, using the function Users.destroy({where: {}}) and then create a new one via the Users.create() function.

We’ll do the same with task creation, but instead of using the function Tasks.create() it will be the function Tasks.bulkCreate(), which allows sending an array of tasks to be inserted in a single execution (this function is very useful for inclusion in a plot of data).

The tasks are going to use the user.id field, created to ensure that they are from the authenticated user. In the end, let’s use the first task created by the piece fakeTask = tasks[0] to reuse its id on the tests that need a task id as a route parameter. We’ll generate a valid token using the function jwt.encode({id: user.id}, jwtSecret).

Both the objects fakeTask and token are created in a scope above the function beforeEach(done), so they can be reused on the tests. To understand in detail, you need to write the following implementation.

 1   beforeEach(done => {
 2     Users
 3       .destroy({where: {}})
 4       .then(() => Users.create({
 5         name: "John",
 6         email: "[email protected]",
 7         password: "12345"
 8       }))
 9       .then(user => {
10         Tasks
11           .destroy({where: {}})
12           .then(() => Tasks.bulkCreate([{
13             id: 1,
14             title: "Work",
15             user_id: user.id
16           }, {
17             id: 2,
18             title: "Study",
19             user_id: user.id
20           }]))
21           .then(tasks => {
22             fakeTask = tasks[0];
23             token = jwt.encode({id: user.id}, jwtSecret);
24             done();
25           });
26      });
27   })

With the pretest routine ready, we are going to write all the tasks tests, starting with the GET / route. Performed a request on it via the request.get("/tasks") function, also using the function set("Authorization", JWT ${token}) to allow sending a header on the request. In this case, the header Authorization is sent along with the value of a valid token.

To make sure the test is successfully accomplished, follow these steps.

  1. Check the status 200 via the expect(200) function.

  2. Apply a simple validation to ensure that the array of size 2 will be returned via the expect(res.body).to.have.length 2) function.

  3. Compare if the titles of the first two tasks are the same as those created by the function Tasks.bulkCreate().

 1   describe("GET /tasks", () => {
 2     describe("status 200", () => {
 3       it("returns a list of tasks", done => {
 4         request.get("/tasks")
 5           .set("Authorization", `JWT ${token}`)
 6           .expect(200)
 7           .end((err, res) => {
 8             expect(res.body).to.have.length(2);
 9             expect(res.body[0].title).to.eql("Work");
10             expect(res.body[1].title).to.eql("Study");
11             done(err);
12           });
13       });
14     });
15   })

To test the successful case of the route POST /tasks, there is no secret: Basically it is a header with an authentication token and a title for a new task. As result, we test if the answer returns the 200 status code and if the object req.body has the same title as the one that was sent to register a new task.

 1   describe("POST /tasks", () => {
 2     describe("status 200", () => {
 3       it("creates a new task", done => {
 4         request.post("/tasks")
 5           .set("Authorization", `JWT ${token}`)
 6           .send({title: "Run"})
 7           .expect(200)
 8           .end((err, res) => {
 9              expect(res.body.title).to.eql("Run");
10              expect(res.body.done).to.be.false;
11              done(err);
12           });
13       });
14     });
15   });

Now we are going to test two simple flows of the route GET /tasks/:id. In the successful case we’ll use the id of the object fakeTask to make sure a valid task will be returned. To test how the application behaves when an id of an invalid task is used, we are going to use the function expect(404) to test the status 404 that indicates if the request did not find a resource .

 1   describe("GET /tasks/:id", () => {
 2     describe("status 200", () => {
 3       it("returns one task", done => {
 4         request.get(`/tasks/${fakeTask.id}`)
 5           .set("Authorization", `JWT ${token}`)
 6           .expect(200)
 7           .end((err, res) => {
 8             expect(res.body.title).to.eql("Work");
 9             done(err);
10           });
11        });
12     });
13     describe("status 404", () => {
14       it("throws error when task not exist", done => {
15         request.get("/tasks/0")
16           .set("Authorization", `JWT ${token}`)
17           .expect(404)
18           .end((err, res) => done(err));
19         });
20     });
21   }

To finish the tests, we are going to test the successful behavior of the routes PUT /tasks/:id and DELETE /tasks/:id. Both of them will use the same functions, except that one test is going to execute the function request.put() and the other one is going to execute the function request.delete(). Both of them, though, expect that the request returns a 204 status code via the expect(204) function.

 1   describe("PUT /tasks/:id", () => {
 2     describe("status 204", () => {
 3       it("updates a task", done => {
 4         request.put(`/tasks/${fakeTask.id}`)
 5           .set("Authorization", `JWT ${token}`)
 6           .send({
 7             title: "Travel",
 8             done: true
 9           })
10           .expect(204)
11           .end((err, res) => done(err));
12       });
13     });
14   });
15   describe("DELETE /tasks/:id", () => {
16     describe("status 204", () => {
17       it("removes a task", done => {
18        request.delete(`/tasks/${fakeTask.id}`)
19          .set("Authorization", `JWT ${token}`)
20          .expect(204)
21          .end((err, res) => done(err));
22       });
23     });
24   })

We have finished the tests of tasks resources. If you execute the command npm test again, you will see the result shown in Figure 9-1.

A435096_1_En_9_Fig1_HTML.jpg
Figure 9-1. Testing a task’s resources

Testing a User’s Endpoints

To test the user’s resource is even simpler, because basically we are going what was explained in the last tests. To start, create the file test/routes/users.js with the following structure.

 1   import jwt from "jwt-simple";
 2
 3   describe("Routes: Tasks", () => {
 4     const Users = app.db.models.Users;
 5     const jwtSecret = app.libs.config.jwtSecret;
 6     let token;
 7     beforeEach(done => {
 8       // Runs before each test...
 9     });
10     describe("GET /user", () => {
11       describe("status 200", () => {
12         it("returns an authenticated user", done => {
13           // Test's logic...
14         });
15       });
16     });
17     describe("DELETE /user", () => {
18       describe("status 204", () => {
19         it("deletes an authenticated user", done => {
20           // Test's logic...
21         });
22       });
23     });
24     describe("POST /users", () => {
25       describe("status 200", () => {
26         it("creates a new user", done => {
27           // Test's logic...
28         });
29       });
30     });
31   });

The pretest logic is going to be simplified, but it will have the generation of a valid token. This code shows how to implement the beforeEach(done) function.

 1   beforeEach(done => {
 2     Users
 3       .destroy({where: {}})
 4       .then(() => Users.create({
 5         name: "John",
 6         email: "[email protected]",
 7         password: "12345"
 8       }))
 9       .then(user => {
10         token = jwt.encode({id: user.id}, jwtSecret);
11         done();
12       });
13   });

Now, to implement these tests, let’s get started testing the GET /user route, which must return the authenticated user’s data, which basically sends a token and receives as a response the user’s data created by the beforeEach(done) function.

 1   describe("GET /user", () => {
 2     describe("status 200", () => {
 3       it("returns an authenticated user", done => {
 4         request.get("/user")
 5           .set("Authorization", `JWT ${token}`)
 6           .expect(200)
 7           .end((err, res) => {
 8             expect(res.body.name).to.eql("John");
 9             expect(res.body.email).to.eql("[email protected]");
10             done(err);
11          });
12       });
13     });
14   })

Then, let’s write the tests to the DELETE /user route. In this case, you simply need to send a token and wait for the 204 status code.

 1   describe("DELETE /user", () => {
 2     describe("status 204", () => {
 3       it("deletes an authenticated user", done => {
 4         request.delete("/user")
 5           .set("Authorization", `JWT ${token}`)
 6           .expect(204)
 7           .end((err, res) => done(err));
 8       });
 9     });
10   });

To finish the last test, we are going to implement the test for new user’s route. This one, doesn’t require a token; after all, it is an open route for new users to register an account in the API. This test’s code is shown here.

 1   describe("POST /users", () => {
 2     describe("status 200", () => {
 3       it("creates a new user", done => {
 4         request.post("/users")
 5           .send({
 6            name: "Mary",
 7            email: "[email protected]",
 8            password: "12345"
 9          })
10          .expect(200)
11          .end((err, res) => {
12            expect(res.body.name).to.eql("Mary");
13            expect(res.body.email).to.eql("[email protected]");
14            done(err);
15          });
16       });
17     });
18   });

Now, if you execute the command npm test again, you’ll see a report like the one shown in Figure 9-2.

A435096_1_En_9_Fig2_HTML.jpg
Figure 9-2. Testing user ’s resources

Conclusion

If you have reached this step, then you have developed a small but powerful API using Node.js and SQL database. Everything is already working and has been tested to ensure the quality of the project code. In the next chapter, we explore a very useful tool for generating API documentation.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset