一步一步搭建react應用-node中使用 mocha + supertest + should 來寫單元測試

對對對就是這發表於2019-02-16

[一步一步構建一個react應用-開篇](https://segmentfault.com/a/11…

git地址

mocha 是一個node單元測試框架,類似於前端的jasmine,語法也相近

supertest 用來測試node介面的庫

should nodejs斷言庫,可讀性很高

  • 搭建

npm install mocha should supertest --save-dev

專案根目錄下新建test資料夾,movies.spec.js

package.json中

"scripts": {
    "start": "pm2 start ecosystem.config.js",
    "test": "mocha --watch" //監聽 test檔案下的所有檔案
  },
  • 介面描述

這裡我們來測試一個新增一條電影的介面

    method: POST
    api:   /api/movies
    document:  {
        "title": `movie0`,
        "thumb": "public/p1075586949.jpg",
        "actors": [
            "河正宇",
            "金允石",
            "鄭滿植"
        ],
        "type": [
            "動作",
            "犯罪"
        ],
        "instruct": `instruct...`,
        "time": "2010-12-22(韓國)",
    }

這裡電影資訊會儲存到movies集合中,型別資訊儲存在types集合中
需要注意的是如果多條電影有相同的type,則同一個電影型別在collection中只存一次,但會inc count欄位

大體程式碼

/routes/movies.js

 const MoviesModel = require(`../models/movies_model`)
 const CONFIG = require(`../config/config`)

 function callback(err, docs, res, next) {
    if (err) {
        next(err)
        return
    }
    res.json({
        code: CONFIG.ERR_OK,
        data: docs
    })
}

 router.post(`/`, function (req, res, next) {
    MoviesModel.addMovies(req.body, (err, docs) => {
        callback(err, docs, res, next)
    })
 });

/models/movies_model.js

    const TypeModel = require(`./type_model`)
    class MoviesModel{

        addMovies(data, callback) {
            const types = data.type
            DB.connect().then((db, err) => {
                TypeModel.addTypes(types, db) //儲存分類
                this.insertOne(db, data, callback)
            }).catch(e => {
                callback(e)
            })
        }
    }

/models/type_model.js

 class Type{

    addTypes(typesArr, db) {
            const Types = db.collection(`types`)
            typesArr.forEach(item => {
                Types.update({
                    `type_name`: item
                }, {
                    `$inc`: { count: 1 }
                }, { upsert: true })
            })
        }

 }
  • 測試

測試中我們錄入兩條電影資訊,兩條的type欄位中會有一個相同的型別

我們要驗證的結論:

兩條電影都成功錄入,types集合中有三條document,”動作”的count是2,另兩條count是1

當前環境是test時,使用測試資料庫

/config/db.js

let db_name=`Movies`
if(process.env.NODE_ENV==`test`){
    db_name=`Movies_test`
}
const url = f(`mongodb://%s:%s@localhost:3307/${db_name}?authMechanism=%s`, user, pwd, authMechanism)

測試資料

const movieInfo = {
    "title": `movie0`,"thumb": "public/p1075586949.jpg",
    "actors": [
        "河正宇",
    ],
    "type": [
        "動作",
        "犯罪"
    ],
    "instruct": `instruct...`,"time": "2010-12-22(韓國)",
}
const movieInfo1 = {
    "title": `movie1`,"thumb": "public/p1075586949.jpg",
    "actors": [
        "河正宇",
    ],
    "type": [
        "動作",
        "愛情"
    ],
    "instruct": `instruct...`,"time": "2010-12-22(韓國)",
}

測試程式碼

process.env.NODE_ENV = `test` //執行時,會將當前環境設定為test,連線資料庫時使用Movies_test庫,如上
const should = require(`should`)
const request = require(`supertest`)
const app = require(`../app`)

describe(`Movies Test`,()=>{

    describe(`POST /movies`,()=>{
        //每個it語句執行開始之前會插入資料
        beforeEach(function (done) {
            request(app) //啟動node服務
                .post(`/api/movies`).send(movieInfo).then(() => {
                    return request(app).post(`/api/movies`).send(movieInfo1)
                }).then(res => {
                    done()
                })
        })

        //每個it語句執行完之後會清除表資料
        afterEach(function (done) {
            MoviesModel.remove(() => {
                TypeModel.remove(() => {
                    done()
                })
            })
       })

        //測試錄入成功
        it(`add movie and get the added movie`, function (done) {
            request(app)
                .get(`/api/movies`)
                .end((er, res) => {
                    should(res.body.data).have.length(2)
                    should(res.body.data[0]).have.property(`title`, `movie1`)
                    done()
                })
        })

        //型別已經存在的就不在存了
        it(`repeat type not saved,will only increment count`, function (done) {
            request(app)
                .get(`/api/types`)
                .then(res => {
                    should(res.body.data).have.length(3)
                    should(res.body.data[0]).have.property(`count`, 2) //"動作"的count是2
                    should(res.body.data[1]).have.property(`count`, 1) 
                    should(res.body.data[2]).have.property(`count`, 1) 
                    done()
                })
        })
    })

})

詳細完整的對每個介面的測試見 test

相關文章