Test environment
Node 中的约定是用 NODE_ENV 环境变量定义应用的执行模式
修改package.json
"scripts": {-- snip --"start": "NODE_ENV=production node index.js","dev": "NODE_ENV=development nodemon index.js","test": "NODE_ENV=test jest --verbose --runInBand"},
—runInBand防止Jest进行运行测试
在脚本中指定应用模式不能在windows上正常工作, 安装cross-env解决这个问题
npm install --save-dev cross-env
再次修改scripts
"scripts": {-- snip --"start": "cross-env NODE_ENV=production node index.js","dev": "cross-env NODE_ENV=development nodemon index.js","test": "cross-env NODE_ENV=test jest --verbose --runInBand",},
修改config.js
require('dotenv').config()const PORT = process.env.PORTconst MONGODB_URI = process.env.NODE_ENV === 'test'? process.env.TEST_MONGODB_URI: process.env.MONGODB_URImodule.exports = {MONGODB_URI,PORT}
修改.env文件, 添加note-app-test数据库
MONGODB_URI=mongodb+srv://fullstack:secred@cluster0-ostce.mongodb.net/note-app?retryWrites=truePORT=3001TEST_MONGODB_URI=mongodb+srv://fullstack:secret@cluster0-ostce.mongodb.net/note-app-test?retryWrites=true
supertest
使用supertest包帮助我们编写API的测试
安装supertest开发依赖
npm install --save-dev supertest
新建test/note_api.test.js
const mongoose = require('mongoose')const supertest = require('supertest')const app = require('../app')const api = supertest(app)test('notes are returned as json', async () => {await api.get('/api/notes').expect(200).expect('Content-Type', /application\/json/)})afterAll(() => {mongoose.connection.close()})
如果测试报错,在项目根目录新建jest.config.js文件
module.exports = {testEnvironment: 'node'}
或者给test添加第三个参数
test('notes are returned as json', async () => {await api.get('/api/notes').expect(200).expect('Content-Type', /application\/json/)}, 100000)
再写一些测试
test('there are two notes', async () => {const response = await api.get('/api/notes')expect(response.body).toHaveLength(2)})test('the first note is about HTTP methods', async () => {const response = await api.get('/api/notes')expect(response.body[0].content).toBe('HTML is easy')})
Initializing the database before tests
在每个test前使用beforeEach初始化数据库
const mongoose = require('mongoose')const supertest = require('supertest')const app = require('../app')const api = supertest(app)const Note = require('../models/note')const initialNotes = [{content: 'HTML is easy',date: new Date(),important: false,},{content: 'Browser can execute only Javascript',date: new Date(),important: true}]beforeEach(async () => {await Note.deleteMany({})let noteObject = new Note(initialNotes[0])await noteObject.save()noteObject = new Note(initialNotes[1])await noteObject.save()})test('notes are returned as json', async () => {await api.get('/api/notes').expect(200).expect('Content-Type', /application\/json/)}, 100000)-- snip --
修改最后2个测试
test('all notes are returned', async () => {const response = await api.get('/api/notes')expect(response.body).toHaveLength(initialNotes.length)})test('a specific note is within the returned notes', async () => {const response = await api.get('/api/notes')const contents = response.body.map(r => r.content)expect(contents).toContain('Browser can execute only Javascript')})
Running tests one by one
运行指定文件的测试
npm test -- tests/note_api.test.js
-t 选项可用于运行具有特定名称的测试
npm test -- -t "a specific note is within the returned notes"
运行包含关键字的测试
npm test -- -t 'notes'
async/await
async/await目的是使用异步调用函数来返回一个 promise,但使代码看起来像是同步调用。
普通的promise
Note.find({}).then(notes => {return notes[0].remove()}).then(response => {console.log('the first note is removed')// more code here})
改用async/await
const notes = await Note.find({})const response = await notes[0].remove()console.log('the first note is removed')
async/await避免了写回调函数
代码执行会在await处暂停等待,直到promise settle了才继续往下执行
需要注意的是: await返回的必须是一个promise, 且只能在async声明的函数中使用
async/await in the backend
notesRouter.get('/', (request, response) => {Note.find({}).then(notes => {response.json(notes)})})
改为使用async/await
notesRouter.get('/', async (request, response) => {const notes = await Note.find({})response.json(notes)})
Error handling and async/await
使用try…catch处理async/await错误
notesRouter.post('/', async (request, response, next) => {const body = request.bodyconst note = new Note({content: body.content,important: body.important || false,date: new Date()})try {const savedNote = await note.save()response.json(savedNote)} catch(exception) {next(exception)}})
Eliminating the try-catch
使用express-async-errors库可以消除try…catch
安装npm install express-async-errors
在app.js中引入库
const config = require('./utils/config')const express = require('express')require('express-async-errors')-- snip --module.exports = app
现在可以直接将try…catch去掉,next(exception)也不再需要,库会自动处理错误
notesRouter.delete('/:id', async (request, response) => {await Note.findByIdAndRemove(request.params.id)response.status(204).end()})
Optimizing the beforeEach function
使用Promise.all
beforeEach(async () => {await Note.deleteMany({})const noteObjects = helper.initialNotes.map(note => new Note(note))const promiseArray = noteObjects.map(note => note.save())await Promise.all(promiseArray)})
await Promise.all会等待所有的promise都完成
