I am trying to create a backend application using Node.js and Express to get post and modify user data from a .json file. Here is my app.js file:
const express = require("express");
const fs = require("fs");
//setting up the express router
const app = express();
app.use(express.json());
//write the code for routes here
app.post("/add", function(req, resp){
var jsonObject = req.body;
var jsonFile = fs.readFileSync("get.json", "UTF8");
var jsonArray = JSON.parse(jsonFile);
jsonArray.push(jsonObject);
jsonFile = JSON.stringify(jsonArray);
resp.json(jsonFile);
fs.writeFileSync("get.json",jsonFile,"utf-8");
});
app.get('/view/:id?', function(req, resp){
var queryURL = url.parse(req.url, true).query;
var jsonFile = fs.readFileSync("get.json", "UTF8");
var data = JSON.parse(jsonFile);
var id = req.params.id;
// if(typeof queryURL.id === "undefined" || queryURL.id == 0){
// resp.json(data);
// }else{
// resp.json(data[queryURL.id-1]);
// }
if (id >= 0) {
data.array.every(user => {
if (user.id === id) {
resp.json(user);
return false;
} else {
console.log("Next")
}
});
} else{
resp.json(data);
}
});
app.patch("/edit/:id", function(req, res){
let userID = req.params.id;
let userFile = fs.readFileSync("get.json", "UTF-8");
let userArray = JSON.parse(userFile);
let reqUserObject = req.body;
let newUserArray = userArray.map(user => {
if (user.id === userID) {
updatedUser = {...user, ...reqUserObject};
return updatedUser;
} else {
return user;
}
});
userFileData = JSON.stringify(newUserArray);
res.json(userFileData);
fs.writeFileSync("get.json", userFileData, "UTF-8");
});
module.exports = app;
All methods work except for
app.get('/view/:id?', function(req, resp)
This Method has an optional query parameter id, if you pass an id you get a user with an id equal to that id. If you dont pass in an id, you get all the users from get.json.
The json file (get.json) I used is:
[{"id":"1","name":"updated name","age":"22","gender":"Male","email":"[email protected]"},{"id":"2","name":"user two","age":"24","gender":"Female","email":"[email protected]"},{"id":"3","name":"user three","age":"23","gender":"Male","email":"[email protected]"},{"id":"4","name":"user four","age":"21","gender":"Male","email":"[email protected]"}]
And my test file is:
const request = require("supertest");
const app = require("../app");
const md5 = require("md5");
const fs = require("fs");
//updating a user profile
test("Updating a user", async () => {
await request(app)
.patch("/edit/1")
.send({
name: "updated name",
})
.expect(200);
setTimeout(() => {
const data = JSON.parse(fs.readFileSync("../post.json"));
expect(data.length).toBe(5);
expect(data[0].name).toBe("updated name");
}, 1000);
});
// // posting a data
test("Posting a new data", async () => {
await request(app)
.post("/add")
.send({
id: "5",
name: "user new",
age: "36",
gender: "Female",
email: "[email protected]",
})
.expect(200);
setTimeout(() => {
const data = JSON.parse(fs.readFileSync("../post.json"));
expect(data.length).toBe(5);
}, 1000);
});
//checking the get route
test("Getting all the user data", async () => {
const response = await request(app).get("/view").expect(200);
expect(response.body.length).toBe(4);
expect(md5(response.body)).toBe("f1d3ff8443297732862df21dc4e57262");
});
//getting profile of a user based on id
test("Getting a single user data", async () => {
const response = await request(app).get("/view?id=2").expect(200);
expect(response.body.length).toBe(1);
expect(md5(response.body)).toBe("93b885adfe0da089cdf634904fd59f71");
});
When I run the tests I get:
rm -rf ./test-report.xml && CI=true ./node_modules/.bin/jest --testResultsProcessor ./node_modules/jest-junit-reporter --forceExit; t-reporter --forceExit;.bin/jest --testResultsProcessor ./node_modules/jest-juni FAIL test/app.test.js ✓ Updating a user (45ms) ✓ Posting a new data (17ms) ✕ Getting all the user data (10ms) ✕ Getting a single user data (4ms)
● Getting all the user data
expected 200 "OK", got 500 "Internal Server Error" 39 | //checking the get route 40 | test("Getting all the user data", async () => { > 41 | const response = await request(app).get("/view").expect(200); | ^ 42 | expect(response.body.length).toBe(4); 43 | expect(md5(response.body)).toBe("f1d3ff8443297732862df21dc4e57262"); 44 | }); at Object.<anonymous>.test (test/app.test.js:41:52) ---- at Test._assertStatus (node_modules/supertest/lib/test.js:252:14) at node_modules/supertest/lib/test.js:306:17 at Test._assertFunction (node_modules/supertest/lib/test.js:285:13) at Test.assert (node_modules/supertest/lib/test.js:164:23) at Server.localAssert (node_modules/supertest/lib/test.js:120:14)
● Getting a single user data
expected 200 "OK", got 500 "Internal Server Error" 46 | //getting profile of a user based on id 47 | test("Getting a single user data", async () => { > 48 | const response = await request(app).get("/view?id=2").expect(200); | ^ 49 | expect(response.body.length).toBe(1); 50 | expect(md5(response.body)).toBe("93b885adfe0da089cdf634904fd59f71"); 51 | }); at Object.<anonymous>.test (test/app.test.js:48:57) ---- at Test._assertStatus (node_modules/supertest/lib/test.js:252:14) at node_modules/supertest/lib/test.js:306:17 at Test._assertFunction (node_modules/supertest/lib/test.js:285:13) at Test.assert (node_modules/supertest/lib/test.js:164:23) at Server.localAssert (node_modules/supertest/lib/test.js:120:14)
Test Suites: 1 failed, 1 total Tests: 2 failed, 2 passed, 4 total Snapshots: 0 total Time: 1.612s Ran all test suites. Force exiting Jest: Have you considered using
--detectOpenHandles
to detect async operations that kept running after all tests finished?
We can see the last 2 tests ("Getting all the user data" and "Getting a single user data") did not pass. These 2 test the app.get('/view/:id?', function(req, resp) method.
How could I fix my app.get('/view/:id?', function(req, resp) method?
Update:
I fixed my app.js according to the suggestion of Yago Biermann, but it is still unable to pass the last 2 tests. My revised app.js:
const express = require("express");
const fs = require("fs");
//setting up the express router
const app = express();
app.use(express.json());
//write the code for routes here
app.post("/add", function(req, resp){
var jsonObject = req.body;
var jsonFile = fs.readFileSync("get.json", "UTF8");
var jsonArray = JSON.parse(jsonFile);
jsonArray.push(jsonObject);
jsonFile = JSON.stringify(jsonArray);
resp.json(jsonFile);
fs.writeFileSync("get.json",jsonFile,"utf-8");
});
app.get('/view', function(req, resp) {
const id = req.query.id;
var jsonFile = fs.readFileSync("get.json", "UTF8");
var data = JSON.parse(jsonFile);
// return the whole data if query parameter wasn't provided
if (!id) return resp.status(200).json(data)
// you should use find instead of every to get the user data
const user = data.find(user => {
if (user.id === id) {
return user;
};
return null;
});
// return the user otherwise return a 404 response
return user ? resp.status(200).json(user) : resp.status(404).json({message:"user not found"})
});
app.patch("/edit/:id", function(req, res){
let userID = req.params.id;
let userFile = fs.readFileSync("get.json", "UTF-8");
let userArray = JSON.parse(userFile);
let reqUserObject = req.body;
let newUserArray = userArray.map(user => {
if (user.id === userID) {
updatedUser = {...user, ...reqUserObject};
return updatedUser;
} else {
return user;
}
});
userFileData = JSON.stringify(newUserArray);
res.json(userFileData);
fs.writeFileSync("get.json", userFileData, "UTF-8");
});
module.exports = app;
And what I get when I run the tests:
rter --forceExit;es/.bin/jest --testResultsProcessor ./node_modules/jest-junit-repo FAIL test/app.test.js ✓ Updating a user (43ms) ✓ Posting a new data (15ms) ✕ Getting all the user data (9ms) ✕ Getting a single user data (4ms)
● Getting all the user data
expect(received).toBe(expected) // Object.is equality Expected: 4 Received: 5 40 | test("Getting all the user data", async () => { 41 | const response = await request(app).get("/view").expect(200); > 42 | expect(response.body.length).toBe(4); | ^ 43 | expect(md5(response.body)).toBe("f1d3ff8443297732862df21dc4e57262"); 44 | }); 45 | at Object.<anonymous>.test (test/app.test.js:42:32)
● Getting a single user data
expect(received).toBe(expected) // Object.is equality Expected: 1 Received: undefined 47 | test("Getting a single user data", async () => { 48 | const response = await request(app).get("/view?id=2").expect(200); > 49 | expect(response.body.length).toBe(1); | ^ 50 | expect(md5(response.body)).toBe("93b885adfe0da089cdf634904fd59f71"); 51 | }); 52 | at Object.<anonymous>.test (test/app.test.js:49:32)
Test Suites: 1 failed, 1 total Tests: 2 failed, 2 passed, 4 total Snapshots: 0 total Time: 1.71s Ran all test suites. Force exiting Jest: Have you considered using
--detectOpenHandles
to detect async operations that kept running after all tests finished?
CodePudding user response:
The problem seems to be at your tests, in your test "Getting all the user data"
you are making a request to /view
but you don't have any route to view, at least it's not in your question, note that /view/:id
is not the same as /view
. Lastly, in your test "Getting a single user data"
you are not providing the id for the user as a url parameter, instead you are passing it as a query parameter, so try to do the following:
test("Getting a single user data", async () => {
// pass the id as url
const response = await request(app).get("/view/2").expect(200);
expect(response.params.id).toBe(2);
expect(response.body.length).toBe(1);
expect(md5(response.body)).toBe("93b885adfe0da089cdf634904fd59f71");
});
See the docs about req.params. Hope I helped a bit!
Edit: As you said that the test file can't be changed, do the following in your view
route:
// Change to view, now the test on route /view should work
app.get('/view', function(req, resp) {
const id = req.query.id;
var jsonFile = fs.readFileSync("get.json", "UTF8");
var data = JSON.parse(jsonFile);
// return the whole data if query parameter wasn't provided
if (!id) return resp.status(200).json(data)
// you should use find instead of every to get the user data
const user = data.find(user => {
if (user.id === id) {
return user;
};
return null;
});
// return the user otherwise return a 404 response
return user ? resp.status(200).json([user]) : resp.status(404).json({message:"user not found"})
});