使用 chai-http 使用 mocha-chai 进行测试期间出现 UnhandledPromiseRejectionWarning
UnhandledPromiseRejectionWarning during testing with mocha-chai using chai-http
制作我的第一个 Express 应用程序,我正在尝试为 api 端点编写测试并使用数据结构作为数据库的占位符,但控制台中不断出现错误,如图所示尽管测试 'passes'
import chai from 'chai';
import { app } from '../app';
import http from 'chai-http';
let expect = chai.expect;
//test I am having problems with
it('should get a single todo at "/api/v1/todos/:id" with GET/id', () => {
return chai
.request(app)
.post('/api/v1/todos')
.send({ title: 'Breakfast', description: 'Breakfast alone' })
.then(resPost => {
expect(resPost).to.have.status(201);
chai
.request(app)
.get('/api/v1/todos/2')
.then(res => {
expect(res).to.have.status(200);
expect(res).to.be.json;
expect(res.body).to.be.an('object');
expect(res.body)
.to.have.property('success')
.eql('true');
expect(res.body)
.to.have.property('message')
.equal('todo retrieved successfully');
expect(res.body.todo).to.be.an('object');
expect(res.body.todo)
.to.have.property('id')
.equal(1);
expect(res.body.todo)
.to.have.property('title')
.equal('Breakfast');
expect(res.body.todo)
.to.have.property('description')
.equal('Breakfast alone');
});
});
});
//api endpoints
//import and define express framework
import express from 'express';
import todoController from '../todoControllers/todo';
//create router handler
const router = express.Router();
router.get('/api/v1/todos', todoController.getAllTodos);
router.get('api/v1/todos/:id', todoController.getTodo);
router.post('/api/v1/todos', todoController.createTodo);
router.put('/api/v1/todos/:id', todoController.updateTodo);
router.delete('/api/v1/todos/:id', todoController.deleteTodo);
export default router;
//controllers for api endpoints
import db from '../db/db';
class todosController {
getAllTodos(req, res) {
return res.status(200).send({
success: 'true',
message: 'todos retrieved successfully',
todos: db
});
}
getTodo(req, res) {
const id = parseInt(req.params.id, 10);
db.map(todo => {
if (todo.id === id) {
return res.status(200).send({
success: 'true',
message: 'todo retrieved successfully',
todo
});
}
});
return res.status(400).send({
success: 'false',
message: 'todo does not exist'
});
}
createTodo(req, res) {
if (!req.body.title) {
return res.status(400).send({
success: 'false',
message: 'title is required'
});
} else if (!req.body.description) {
return res.status(400).send({
success: 'false',
message: 'description is required'
});
}
const todo = {
id: db.length + 1,
title: req.body.title,
description: req.body.description
};
db.push(todo);
return res.status(201).send({
success: 'true',
message: 'todo added successfully',
todo
});
}
updateTodo(req, res) {
const id = parseInt(req.params.id, 10);
let todoFound;
let itemIndex;
db.map((todo, index) => {
if (todo.id === id) {
todoFound = todo;
itemIndex = index;
}
});
if (!todoFound) {
return res.status(404).send({
success: 'true',
message: 'todo not found'
});
}
if (!req.body.title) {
return res.status(400).send({
success: 'false',
message: 'title is required'
});
} else if (!req.body.description) {
return res.status(400).send({
success: 'false',
message: 'description is required'
});
}
const editedTodo = {
// @ts-ignore
id: todoFound.id,
// @ts-ignore
title: req.body.title || todoFound.title,
// @ts-ignore
description: req.body.description || todoFound.description
};
db.splice(itemIndex, 1, editedTodo);
return res.status(200).send({
success: 'true',
message: 'todo updated successfully',
editedTodo
});
}
deleteTodo(req, res) {
const id = parseInt(req.params.id, 10);
let todoFound;
let itemIndex;
db.map((todo, index) => {
if (todo.id === id) {
todoFound = todo;
itemIndex = index;
}
});
if (!todoFound) {
return res.status(404).send({
success: 'true',
message: 'todo not found'
});
}
db.splice(itemIndex, 1);
return res.status(200).send({
success: 'true',
message: 'todo deleted successfully'
});
}
}
const todoController = new todosController();
export default todoController;
//place-holder database in the form of datasctructures
const todos = [
{
id: 1,
title: 'lunch',
description: 'Go for lunch by 2pm'
}
];
//Creating module from data structure and exposing it to be used by parts of the server
export default todos;
//tests written so far including the initial snippet
import chai from 'chai';
import { app } from '../app';
import http from 'chai-http';
import db from '../db/db';
let expect = chai.expect;
chai.use(http);
describe('Test all todo endpoints at "/api/v1/todos and "/api/v1/todo/:id" with (GET, POST, GET/id, PUT)', () => {
before(() => {});
after(() => {});
//GET all todos
it('should get all todos at "/ap1/v1/todos" with GET', () => {
return chai
.request(app)
.get('/api/v1/todos')
.then(res => {
expect(res).to.have.status(200);
expect(res).to.be.json;
expect(res.body).to.be.an('object');
expect(res.body)
.to.have.property('success')
.eql('true');
expect(res.body)
.to.have.property('message')
.eql('todos retrieved successfully');
expect(res.body.todos).to.be.an('array');
expect(
res.body.todos[Math.floor(Math.random() * res.body.todos.length)]
).to.have.property('id' && 'title' && 'description');
});
});
//POST a todo
it('should add a todo at "/api/v1/todos" with POST', () => {
return chai
.request(app)
.post('/api/v1/todos')
.send({ title: 'Dinner', description: 'Dinner with bae' })
.then(res => {
expect(res).to.have.status(201);
expect(res).to.be.json;
expect(res.body).to.be.an('object');
expect(res.body)
.to.have.property('success')
.eql('true');
expect(res.body)
.to.have.property('message')
.equal('todo added successfully');
expect(res.body.todo).to.be.an('object');
expect(res.body.todo)
.to.have.property('id')
.equal(db.length);
expect(res.body.todo)
.to.have.property('title')
.equal('Dinner');
expect(res.body.todo)
.to.have.property('description')
.equal('Dinner with bae');
});
});
//corrected test still giving an 'AssertionError'
it('should get a single todo at "/api/v1/todos/:id" with GET/id', () => {
return chai
.request(app)
.post('/api/v1/todos')
.send({ title: 'Breakfast', description: 'Breakfast alone' })
.then(resPost => {
expect(resPost).to.have.status(201);
return chai
.request(app)
.get('/api/v1/todos/2')
.then(res => {
expect(res).to.have.status(200);
expect(res).to.be.json;
expect(res.body).to.be.an('object');
expect(res.body)
.to.have.property('success')
.eql('true');
expect(res.body)
.to.have.property('message')
.equal('todo retrieved successfully');
expect(res.body.todo).to.be.an('object');
expect(res.body.todo)
.to.have.property('id')
.equal(1);
expect(res.body.todo)
.to.have.property('title')
.equal('Breakfast');
expect(res.body.todo)
.to.have.property('description')
.equal('Breakfast alone');
});
});
});
});
谁能解释一下为什么?
P.S :我更新了问题以显示我的 api 端点、测试和数据库占位符,我基本上需要提示如何处理涉及和 id 的请求。
你有两个问题:
尽管出现断言错误,但您的测试并未失败
这是因为您在第 14 行中缺少 return
。由于您在第 12 行注册的函数没有 return 任何内容,因此承诺得到解决。结果,您在第 8 行 return 的承诺也得到解决,并且您的测试没有失败,尽管它应该失败。
由于您没有 return 被拒绝的承诺,因此不会传播错误。这就是您收到错误消息的原因。
断言错误本身
您正在创建资源,然后假定其 ID 为 2 来请求该资源。您收到 404 响应,这意味着该资源根本未创建或具有不同的 ID。
在创建资源时,您以某种方式为其分配了一个 ID(看起来您只是为此使用了一个计数器,但如果没有看到您的实现,我无法确定)。 POST 响应应提供有关此 ID 的一些信息。理想情况下,它将 id 作为位置 header 发送,但通常完整创建的资源作为响应 body 发送。因此,在发出 GET 请求之前,您需要从 POST 响应中提取 id。
制作我的第一个 Express 应用程序,我正在尝试为 api 端点编写测试并使用数据结构作为数据库的占位符,但控制台中不断出现错误,如图所示尽管测试 'passes'
import chai from 'chai';
import { app } from '../app';
import http from 'chai-http';
let expect = chai.expect;
//test I am having problems with
it('should get a single todo at "/api/v1/todos/:id" with GET/id', () => {
return chai
.request(app)
.post('/api/v1/todos')
.send({ title: 'Breakfast', description: 'Breakfast alone' })
.then(resPost => {
expect(resPost).to.have.status(201);
chai
.request(app)
.get('/api/v1/todos/2')
.then(res => {
expect(res).to.have.status(200);
expect(res).to.be.json;
expect(res.body).to.be.an('object');
expect(res.body)
.to.have.property('success')
.eql('true');
expect(res.body)
.to.have.property('message')
.equal('todo retrieved successfully');
expect(res.body.todo).to.be.an('object');
expect(res.body.todo)
.to.have.property('id')
.equal(1);
expect(res.body.todo)
.to.have.property('title')
.equal('Breakfast');
expect(res.body.todo)
.to.have.property('description')
.equal('Breakfast alone');
});
});
});
//api endpoints
//import and define express framework
import express from 'express';
import todoController from '../todoControllers/todo';
//create router handler
const router = express.Router();
router.get('/api/v1/todos', todoController.getAllTodos);
router.get('api/v1/todos/:id', todoController.getTodo);
router.post('/api/v1/todos', todoController.createTodo);
router.put('/api/v1/todos/:id', todoController.updateTodo);
router.delete('/api/v1/todos/:id', todoController.deleteTodo);
export default router;
//controllers for api endpoints
import db from '../db/db';
class todosController {
getAllTodos(req, res) {
return res.status(200).send({
success: 'true',
message: 'todos retrieved successfully',
todos: db
});
}
getTodo(req, res) {
const id = parseInt(req.params.id, 10);
db.map(todo => {
if (todo.id === id) {
return res.status(200).send({
success: 'true',
message: 'todo retrieved successfully',
todo
});
}
});
return res.status(400).send({
success: 'false',
message: 'todo does not exist'
});
}
createTodo(req, res) {
if (!req.body.title) {
return res.status(400).send({
success: 'false',
message: 'title is required'
});
} else if (!req.body.description) {
return res.status(400).send({
success: 'false',
message: 'description is required'
});
}
const todo = {
id: db.length + 1,
title: req.body.title,
description: req.body.description
};
db.push(todo);
return res.status(201).send({
success: 'true',
message: 'todo added successfully',
todo
});
}
updateTodo(req, res) {
const id = parseInt(req.params.id, 10);
let todoFound;
let itemIndex;
db.map((todo, index) => {
if (todo.id === id) {
todoFound = todo;
itemIndex = index;
}
});
if (!todoFound) {
return res.status(404).send({
success: 'true',
message: 'todo not found'
});
}
if (!req.body.title) {
return res.status(400).send({
success: 'false',
message: 'title is required'
});
} else if (!req.body.description) {
return res.status(400).send({
success: 'false',
message: 'description is required'
});
}
const editedTodo = {
// @ts-ignore
id: todoFound.id,
// @ts-ignore
title: req.body.title || todoFound.title,
// @ts-ignore
description: req.body.description || todoFound.description
};
db.splice(itemIndex, 1, editedTodo);
return res.status(200).send({
success: 'true',
message: 'todo updated successfully',
editedTodo
});
}
deleteTodo(req, res) {
const id = parseInt(req.params.id, 10);
let todoFound;
let itemIndex;
db.map((todo, index) => {
if (todo.id === id) {
todoFound = todo;
itemIndex = index;
}
});
if (!todoFound) {
return res.status(404).send({
success: 'true',
message: 'todo not found'
});
}
db.splice(itemIndex, 1);
return res.status(200).send({
success: 'true',
message: 'todo deleted successfully'
});
}
}
const todoController = new todosController();
export default todoController;
//place-holder database in the form of datasctructures
const todos = [
{
id: 1,
title: 'lunch',
description: 'Go for lunch by 2pm'
}
];
//Creating module from data structure and exposing it to be used by parts of the server
export default todos;
//tests written so far including the initial snippet
import chai from 'chai';
import { app } from '../app';
import http from 'chai-http';
import db from '../db/db';
let expect = chai.expect;
chai.use(http);
describe('Test all todo endpoints at "/api/v1/todos and "/api/v1/todo/:id" with (GET, POST, GET/id, PUT)', () => {
before(() => {});
after(() => {});
//GET all todos
it('should get all todos at "/ap1/v1/todos" with GET', () => {
return chai
.request(app)
.get('/api/v1/todos')
.then(res => {
expect(res).to.have.status(200);
expect(res).to.be.json;
expect(res.body).to.be.an('object');
expect(res.body)
.to.have.property('success')
.eql('true');
expect(res.body)
.to.have.property('message')
.eql('todos retrieved successfully');
expect(res.body.todos).to.be.an('array');
expect(
res.body.todos[Math.floor(Math.random() * res.body.todos.length)]
).to.have.property('id' && 'title' && 'description');
});
});
//POST a todo
it('should add a todo at "/api/v1/todos" with POST', () => {
return chai
.request(app)
.post('/api/v1/todos')
.send({ title: 'Dinner', description: 'Dinner with bae' })
.then(res => {
expect(res).to.have.status(201);
expect(res).to.be.json;
expect(res.body).to.be.an('object');
expect(res.body)
.to.have.property('success')
.eql('true');
expect(res.body)
.to.have.property('message')
.equal('todo added successfully');
expect(res.body.todo).to.be.an('object');
expect(res.body.todo)
.to.have.property('id')
.equal(db.length);
expect(res.body.todo)
.to.have.property('title')
.equal('Dinner');
expect(res.body.todo)
.to.have.property('description')
.equal('Dinner with bae');
});
});
//corrected test still giving an 'AssertionError'
it('should get a single todo at "/api/v1/todos/:id" with GET/id', () => {
return chai
.request(app)
.post('/api/v1/todos')
.send({ title: 'Breakfast', description: 'Breakfast alone' })
.then(resPost => {
expect(resPost).to.have.status(201);
return chai
.request(app)
.get('/api/v1/todos/2')
.then(res => {
expect(res).to.have.status(200);
expect(res).to.be.json;
expect(res.body).to.be.an('object');
expect(res.body)
.to.have.property('success')
.eql('true');
expect(res.body)
.to.have.property('message')
.equal('todo retrieved successfully');
expect(res.body.todo).to.be.an('object');
expect(res.body.todo)
.to.have.property('id')
.equal(1);
expect(res.body.todo)
.to.have.property('title')
.equal('Breakfast');
expect(res.body.todo)
.to.have.property('description')
.equal('Breakfast alone');
});
});
});
});
谁能解释一下为什么? P.S :我更新了问题以显示我的 api 端点、测试和数据库占位符,我基本上需要提示如何处理涉及和 id 的请求。
你有两个问题:
尽管出现断言错误,但您的测试并未失败
这是因为您在第 14 行中缺少 return
。由于您在第 12 行注册的函数没有 return 任何内容,因此承诺得到解决。结果,您在第 8 行 return 的承诺也得到解决,并且您的测试没有失败,尽管它应该失败。
由于您没有 return 被拒绝的承诺,因此不会传播错误。这就是您收到错误消息的原因。
断言错误本身
您正在创建资源,然后假定其 ID 为 2 来请求该资源。您收到 404 响应,这意味着该资源根本未创建或具有不同的 ID。
在创建资源时,您以某种方式为其分配了一个 ID(看起来您只是为此使用了一个计数器,但如果没有看到您的实现,我无法确定)。 POST 响应应提供有关此 ID 的一些信息。理想情况下,它将 id 作为位置 header 发送,但通常完整创建的资源作为响应 body 发送。因此,在发出 GET 请求之前,您需要从 POST 响应中提取 id。