I am trying to build a MERN website and implement unit tests for its POST method. But it gives me the error "For async tests and hooks, ensure "done()" is called;" ? Anyone can you please give me some idea why does this error occur? and How can I resolve this issue?
error:
- POST /products OK, creating a new product works: Error: Timeout of 30000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves. (/home/nimeshaf/Documents/mernPro/MERN-project/test/api/product/post.js) at listOnTimeout (node:internal/timers:557:17)
I have attached my files below
MERNProject/server.js
const express = require("express");
const mongoose = require("mongoose");
const bodyParser = require("body-parser");
const cors = require("cors");
const app = express();
//import routes
const postRoutes = require("./routes/products");
//app middleware
app.use(bodyParser.json());
app.use(cors());
app.use(postRoutes);
const PORT = 8000;
const DB_URL =
"mongodb srv://twg:[email protected]/myFirstDatabase?retryWrites=true&w=majority";
function connect() {
return new Promise((resolve, reject) => {
mongoose
.connect(DB_URL)
.then((res, err) => {
if (err) return reject(err);
//console.log(`DB connected`);
resolve();
})
.catch((err) => console.log(`DB connection error`, err));
});
}
function close() {
return mongoose.disconnect();
}
connect();
app.listen(PORT, () => {
console.log(`App is running on ${PORT}`);
});
module.exports = { connect, close };
MERNProject/test/api/products/post.js
const expect = require("chai").expect;
const request = require("supertest");
const app = require("../../../routes/products.js");
const conn = require("../../../server.js");
describe("POST /products", () => {
before((done) => {
conn
.connect()
.then(() => done())
.catch((err) => done(err));
});
after((done) => {
conn
.close()
.then(() => done())
.catch((err) => done(err));
});
it("OK, creating a new product works", (done) => {
request(app)
.post("/product/save")
.send({
productName: "Mango",
description: "description Mango",
price: "rs 50",
productCategory: "fruits",
productUrl:
"https://upload.wikimedia.org/wikipedia/commons/9/90/Hapus_Mango.jpg",
})
.then((res) => {
const body = res.body;
expect(body).to.contain.porperty("_id");
expect(body).to.contain.porperty("productName");
expect(body).to.contain.porperty("description");
expect(body).to.contain.porperty("price");
expect(body).to.contain.porperty("productCategory");
expect(body).to.contain.porperty("productUrl");
done();
});
});
});
MERNProject/routes/products.js
const express = require("express");
const Products = require("../models/products");
const router = express();
//save Products
router.post("/product/save", (err, req, res, next) => {
let newProduct = new Products(req.body);
newProduct.save((err) => {
if (err) {
return res.status(400).json({
error: err,
});
}
return res.status(200).json({
success: "Product saved successfully",
});
});
});
//get products
router.get("/products", (req, res) => {
Products.find().exec((err, products) => {
if (err) {
return res.status(400).json({
error: err,
});
}
return res.status(200).json({
success: true,
existingProducts: products,
});
});
});
//update products
router.put("/product/update/:id", (req, res) => {
Products.findByIdAndUpdate(
req.params.id,
{
$set: req.body,
},
(err, product) => {
if (err) {
return res.status(400).json({ error: err });
}
return res.status(200).json({
success: "Updated Successfully",
});
}
);
});
//delete product
router.delete("/product/delete/:id", (req, res) => {
Products.findByIdAndRemove(req.params.id).exec((err, deleteProduct) => {
if (err)
return res.status(400).json({
message: "Delete unsuccessful",
err,
});
return res.json({
message: "Delete Successfully",
deleteProduct,
});
});
});
//get specific product
router.get("/product/:id", (req, res) => {
let productId = req.params.id;
Products.findById(productId, (err, product) => {
if (err) {
return res.status(400).json({ success: false, err });
}
return res.status(200).json({
success: true,
product,
});
});
});
module.exports = router;
MERNProject/package.json
{
"name": "mern_crud",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"server": "nodemon server.js",
"client": "npm run start --prefix client",
"dev": "concurrently \"npm run server\" \"npm run client\"",
"test": "mocha --recursive --timeout 30000 --exit"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"body-parser": "^1.19.2",
"chai": "^4.3.6",
"concurrently": "^7.0.0",
"cors": "^2.8.5",
"express": "^4.17.3",
"mocha": "^9.2.2",
"mockgoose": "^8.0.4",
"mongoose": "^6.2.7",
"mongose": "^0.0.2-security",
"nodemon": "^2.0.15",
"react-router-dom": "^6.2.2",
"supertest": "^6.2.2"
}
}
CodePudding user response:
Your issue probably comes from the spec OK, creating a new product works
where the done
callback is not called in case of failure in the promises chain. You need to add a catch
:
request(app)
.post("/product/save")
.send(/* ... */)
.then((res) => {
// ...
done();
})
.catch((err) => done(err)); // this is missing
That being said, I would strongly recommend using the async/await
syntax instead of the done
callback:
it("OK, creating a new product works", async () => {
const res = await request(app)
.post("/product/save")
.send(/* ... */);
const body = res.body;
expect(body).to.contain.porperty("_id");
expect(body).to.contain.porperty("productName");
expect(body).to.contain.porperty("description");
expect(body).to.contain.porperty("price");
expect(body).to.contain.porperty("productCategory");
expect(body).to.contain.porperty("productUrl");
});