- Published on
เขียน Test API Node.js ด้วย Jest และใช้ DB เป็น Mongo
สวัสดีครับ ผมจะลองยกตัวอย่างการเขียน Test สั้นๆด้วย Jest โปรเจค Node ของผมใช้ Express.js และ DB เป็น MongoDB ส่วนของการ Test เป็นการเขียน Test API ครับ
Install - Jest
npm i jest
Install - Supertest
ใช้สำหรับ Call API ตอน Run test
npm i supertest
Install - Mongodb Memory
เราจะใช้ตัวนี้เพื่อเป็นการ Mock mongoDB ขึ้นมาตอน Run test ถ้าใน Function ที่เรา Test มีการต่อ DB ตอน Run test เราจะไม่ต่อ DB จริงๆ
npm i mongodb-memory-server
สร้าง connection สำหรับ mongodb memory
ใช้สำหรับ connect , clear และ close mongoDB
const mongoose = require("mongoose")
const {MongoMemoryServer} = require("mongodb-memory-server")
module.exports.connect = async () => {
const mongoDb = await MongoMemoryServer.create()
const uri = mongoDb.getUri()
const mongooseOpts = {
useNewUrlParser: true,
useUnifiedTopology: true,
maxPoolSize: 10,
}
await mongoose.connect(uri, mongooseOpts)
}
module.exports.closeDatabase = async () => {
await mongoose.connection.dropDatabase()
await mongoose.connection.close()
}
module.exports.clearDatabase = async () => {
const collections = mongoose.connection.collections
for (const key in collections) {
const collection = collections[key]
await collection.deleteMany()
}
}
npm test
แก้ไข config เมื่อใช้ "test": "jest --watchAll --coverage "
- --watchAll - Run ทุก test
- --coverage - แสดง % การ test ของแต่ล่ะไฟล์ว่า test ไปกี่ % แล้ว และแสดงด้วยว่าแต่ล่ะไฟล์มี line ไหนที่ยังไม่ได้ test
Function ที่เราจะ Test
exports.signup = async (req, res) => {
const countF = await Farm.collection.countDocuments()
console.log("farm count : ", countF)
const farm = new Farm({
code: "F" + String(countF + 1).padStart(4, "0"),
name: req.body.farmName,
lineToken: null,
})
const user = new User({
username: req.body.username,
password: bcrypt.hashSync(req.body.password, 8),
farm: farm,
})
const farmResp = await farm.save()
console.log("farm saved : ", farmResp)
const userResp = await user.save()
console.log("user saved : ", userResp)
res.status(200).send({message: "Registered Successfully."})
}
สร้างไฟล์สำหรับ Test
ต้องสร้างชื่อไฟล์ตามด้วย .test เสมอ เพื่อให้ jest มองเห็น เมื่อ run มันจะวิ่งหาไฟล์ที่มี .test ทั้งหมด
const express = require("express")
const request = require("supertest")
const authController = require("../controllers/auth.controller")
const {verifySignUp} = require("../middlewares")
const db = require("../config/conn.memory")
//เรียก function นี้ก่อน run test ทั้งหมดสำหรับไฟล์นี้
beforeAll(async () => {
//เชื่อมต่อ db memory
await db.connect()
})
//เรียก function นี้ทุกครั้งหลัง run เสร็จแต่ล่ะ test
afterEach(async () => {
//เคลียร์ข้อมูลทั้งหมดใน db memory
await db.clearDatabase()
})
//เรียก function นี้เมื่อ run ครบทุก test
afterAll(async () => {
//ยกเลิกเชื่อมต่อ db และ drop db ทิ้ง
await db.closeDatabase()
})
//อธิบายว่ากลุ่มการ test นี้ทำอะไร
describe("Signup ", () => {
let app
//เรียก Function นี้ก่อนจะ run แต่ล่ะ test
beforeEach(() => {
app = express()
app.use(express.json())
//กำหนด Path API ที่เราจะ Test
//API นี้มี Middleware check dupplicate username ด้วย
app.post("/signup", [verifySignUp.checkDuplicateUsername], authController.signup)
})
//Test นี้คือต้องสร้าง user สำเร็จ
test("Should create a new user", async () => {
const res = await request(app).post("/signup").send({
username: "signuptest",
farmName: "signuptest",
password: "P@ssw0rd",
})
//status ต้องเท่ากับ 200
expect(res.status).toEqual(200)
//body ต้องได้ { message : 'Registered Successfully.'}
expect(res.body).toHaveProperty("message", "Registered Successfully.")
})
//Test นี้คือต้องสร้าง user ไม่สำเร็จ เพราะมีการใช้ username ซ้ำ
test("Should return error if user already exists", async () => {
await request(app).post("/signup").send({
username: "signuptest",
farmName: "signuptest",
password: "P@ssw0rd",
})
const res = await request(app).post("/signup").send({
username: "signuptest",
farmName: "signuptest",
password: "P@ssw0rd",
})
//status ต้องเท่ากับ 400
expect(res.status).toEqual(400)
//body ต้องได้ { message : 'Failed! Username is already in use!'}
expect(res.body).toHaveProperty("message", "Failed! Username is already in use!")
})
})
สามารถเข้าไปดูการใช้ expect
เพิ่มเติมได้ที่ https://jestjs.io/docs/expect
Run
npm test
- กรณีผ่านทุก Test
จะเห็นว่าแสดงผล % การ Test ทุกไฟล์ที่มีการ Test เลย และในคอลัมน์สุดท้าย Uncoverd Line บอกด้วยว่าบรรทัดที่เท่าไหร่บ้างที่ไม่ถูก Test ซึ่งเราก็ควรจะเขียน Test case ให้ครบจนกว่าจะเป็น 100 และเขียวหมด แสดงว่า Code ทุกบรรทัดที่เราเขียนได้ถูก Test หมดแล้ว
- กรณีมี Test fail
กรณี Run test ไม่ผ่าน jest จะบอกเลย value ที่ได้มาคืออะไร แล้ว expect อะไร fail ที่บรรทัดไหน ในรูปคือ return status 400 มาแต่เรา expect 500 ทำให้ Test ไม่ผ่าน