การทดสอบเป็นส่วนสำคัญในการสร้างแอปพลิเคชัน Node.js ที่มีประสิทธิภาพ การทดสอบที่เหมาะสมสามารถเอาชนะข้อบกพร่องมากมายที่นักพัฒนาอาจชี้ให้เห็นได้อย่างง่ายดาย โซลูชันการพัฒนา Node.js .
ในขณะที่นักพัฒนาจำนวนมากมุ่งเน้นไปที่การครอบคลุม 100% ด้วยการทดสอบหน่วย แต่สิ่งสำคัญคือโค้ดที่คุณเขียนไม่ได้ถูกทดสอบเพียงอย่างเดียว การผสานรวมและการทดสอบแบบ end-to-end ช่วยให้คุณมั่นใจมากขึ้นโดยการทดสอบส่วนต่างๆของแอปพลิเคชันของคุณร่วมกัน ชิ้นส่วนเหล่านี้อาจทำงานได้ดีในตัวมันเอง แต่ในระบบขนาดใหญ่หน่วยของโค้ดแทบจะไม่ทำงานแยกกัน
Node.js และ MongoDB รวมกันเป็นหนึ่งใน duos ยอดนิยมในช่วงเวลาล่าสุด หากคุณเป็นหนึ่งในหลาย ๆ คนที่ใช้พวกเขาแสดงว่าคุณโชคดี
ในบทความนี้คุณจะได้เรียนรู้วิธีการเขียนการรวมและการทดสอบ end-to-end อย่างง่ายดายสำหรับแอปพลิเคชัน Node.js และ MongoDB ของคุณที่ทำงานบนอินสแตนซ์จริงของฐานข้อมูลทั้งหมดโดยไม่จำเป็นต้องตั้งค่าสภาพแวดล้อมที่ซับซ้อนหรือโค้ดการตั้งค่า / การฉีกขาดที่ซับซ้อน .
คุณจะเห็นว่าแพ็กเกจหน่วย mongo ช่วยในการผสานรวมและการทดสอบแบบ end-to-end ใน Node.js ได้อย่างไร สำหรับภาพรวมที่ครอบคลุมมากขึ้นของการทดสอบการรวม Node.js โปรดดู บทความนี้ .
โดยปกติแล้วสำหรับการทดสอบการรวมหรือ end-to-end สคริปต์ของคุณจะต้องเชื่อมต่อกับฐานข้อมูลเฉพาะจริงเพื่อวัตถุประสงค์ในการทดสอบ สิ่งนี้เกี่ยวข้องกับการเขียนโค้ดที่รันที่จุดเริ่มต้นและจุดสิ้นสุดของทุกกรณี / ชุดทดสอบเพื่อให้แน่ใจว่าฐานข้อมูลอยู่ในสถานะที่คาดเดาได้ชัดเจน
สิ่งนี้อาจใช้ได้ดีสำหรับบางโครงการ แต่มีข้อ จำกัด บางประการ:
ในทางกลับกันการใช้ฐานข้อมูลจริงทำให้สภาพแวดล้อมการทดสอบใกล้เคียงกับการผลิตมากที่สุด สิ่งนี้สามารถมองได้ว่าเป็นข้อดีเฉพาะของแนวทางนี้
การใช้ฐานข้อมูลจริงในการทดสอบดูเหมือนจะมีความท้าทายอยู่บ้าง แต่ข้อดีของการใช้ฐานข้อมูลจริงนั้นดีเกินกว่าจะส่งต่อได้ เราจะหลีกเลี่ยงความท้าทายและรักษาความได้เปรียบได้อย่างไร
การนำโซลูชันที่ดีจากแพลตฟอร์มอื่นมาใช้ซ้ำและนำไปใช้กับโลก Node.js อาจเป็นหนทางไปที่นี่
คุณลักษณะใน python คืออะไร
โปรเจ็กต์ Java ใช้ DBUnit กับฐานข้อมูลในหน่วยความจำอย่างกว้างขวาง (เช่น H2) เพื่อจุดประสงค์นี้
DBUnit ถูกรวมเข้ากับ JUnit (ตัวทดสอบ Java) และช่วยให้คุณกำหนดสถานะฐานข้อมูลสำหรับชุดทดสอบ / ชุดทดสอบแต่ละชุด ฯลฯ ซึ่งจะลบข้อ จำกัด ที่กล่าวถึงข้างต้น:
จากแนวคิดเหล่านี้ฉันตัดสินใจทำสิ่งที่คล้ายกันสำหรับ Node.js และ MongoDB: หน่วย Mongo .
Mongo-unit คือแพ็คเกจ Node.js ที่สามารถติดตั้งโดยใช้ NPM หรือ Yarn มันเรียกใช้ MongoDB ในหน่วยความจำ ทำให้การทดสอบการรวมเป็นเรื่องง่ายโดยการรวมเข้ากับ Mocha และจัดเตรียม API ที่เรียบง่ายเพื่อจัดการสถานะฐานข้อมูล
ไลบรารีใช้ไฟล์ mongodb- สร้างไว้ล่วงหน้า แพ็คเกจ NPM ซึ่งมีไบนารี MongoDB ที่สร้างไว้ล่วงหน้าสำหรับระบบปฏิบัติการยอดนิยม อินสแตนซ์ MongoDB เหล่านี้สามารถทำงานในโหมดในหน่วยความจำ
ในการเพิ่มหน่วย mongo ในโครงการของคุณคุณสามารถเรียกใช้:
npm install -D mongo-unit
หรือ
yarn add mongo-unit
และนั่นคือมัน คุณไม่จำเป็นต้องติดตั้ง MongoDB บนคอมพิวเตอร์ของคุณเพื่อใช้แพ็คเกจนี้
สมมติว่าคุณมีแอปพลิเคชัน Node.js ที่เรียบง่ายเพื่อจัดการงาน:
// service.js const mongoose = require('mongoose') const mongoUrl = process.env.MONGO_URL || 'mongodb://localhost:27017/example' mongoose.connect(mongoUrl) const TaskSchema = new mongoose.Schema({ name: String, started: Date, completed: Boolean, }) const Task = mongoose.model('tasks', TaskSchema) module.exports = { getTasks: () => Task.find(), addTask: data => new Task(data).save(), deleteTask: taskId => Task.findByIdAndRemove(taskId) }
URL การเชื่อมต่อ MongoDB ไม่ได้ถูกเข้ารหัสไว้ที่นี่ เช่นเดียวกับ back-end ของเว็บแอปพลิเคชันส่วนใหญ่เรากำลังนำมันมาจากตัวแปรสภาพแวดล้อม สิ่งนี้จะช่วยให้เราแทนที่ URL ใดก็ได้ในระหว่างการทดสอบ
const express = require('express') const bodyParser = require('body-parser') const service = require('./service') const app = express() app.use(bodyParser.json()) app.use(express.static(`${__dirname}/static`)) app.get('/example', (req, res) => { service.getTasks().then(tasks => res.json(tasks)) }) app.post('/example', (req, res) => { service.addTask(req.body).then(data => res.json(data)) }) app.delete('/example/:taskId', (req, res) => { service.deleteTask(req.params.taskId).then(data => res.json(data)) }) app.listen(3000, () => console.log('started on port 3000'))
นี่คือตัวอย่างของแอปพลิเคชันตัวอย่างที่มีอินเทอร์เฟซผู้ใช้ รหัสสำหรับ UI ถูกละไว้เพื่อความกะทัดรัด คุณสามารถดูตัวอย่างทั้งหมดได้ บน GitHub .
ในการทำให้ Mocha รันการทดสอบการรวมกับหน่วย mongo เราจำเป็นต้องรันอินสแตนซ์ฐานข้อมูลหน่วย mongo ก่อนที่จะโหลดรหัสแอปพลิเคชันในบริบท Node.js ในการทำเช่นนี้เราสามารถใช้ mocha --require
พารามิเตอร์และไลบรารี Mocha-เตรียมซึ่งช่วยให้คุณสามารถดำเนินการแบบอะซิงโครนัสในสคริปต์ที่ต้องการ
// it-helper.js const prepare = require('mocha-prepare') const mongoUnit = require('mongo-unit') prepare(done => mongoUnit.start() .then(testMongoUrl => { process.env.MONGO_URL = testMongoUrl done() }))
ขั้นตอนแรกคือการเพิ่มการทดสอบในฐานข้อมูลการทดสอบ (testData.json
):
{ 'tasks': [ { 'name': 'test', 'started': '2017-08-28T16:07:38.268Z', 'completed': false } ] }
ขั้นตอนต่อไปคือการเพิ่มการทดสอบด้วยตนเอง:
const expect = require('chai').expect const mongoose = require('mongoose') const mongoUnit = require('../index') const service = require('./app/service') const testMongoUrl = process.env.MONGO_URL describe('service', () => { const testData = require('./fixtures/testData.json') beforeEach(() => mongoUnit.initDb(testMongoUrl, testData)) afterEach(() => mongoUnit.drop()) it('should find all tasks', () => { return service.getTasks() .then(tasks => { expect(tasks.length).to.equal(1) expect(tasks[0].name).to.equal('test') }) }) it('should create new task', () => { return service.addTask({ name: 'next', completed: false }) .then(task => { expect(task.name).to.equal('next') expect(task.completed).to.equal(false) }) .then(() => service.getTasks()) .then(tasks => { expect(tasks.length).to.equal(2) expect(tasks[1].name).to.equal('next') }) }) it('should remove task', () => { return service.getTasks() .then(tasks => tasks[0]._id) .then(taskId => service.deleteTask(taskId)) .then(() => service.getTasks()) .then(tasks => { expect(tasks.length).to.equal(0) }) }) })
และ voila!
สังเกตว่ามีโค้ดเพียงสองสามบรรทัดที่เกี่ยวข้องกับการตั้งค่าและการฉีกขาด
อย่างที่คุณเห็นมันง่ายมากที่จะเขียนการทดสอบการรวมโดยใช้ไลบรารีหน่วย mongo เราไม่ได้ล้อเลียน MongoDB ตัวเองและเราสามารถใช้โมเดลพังพอนเดียวกันได้ เราสามารถควบคุมข้อมูลฐานข้อมูลได้อย่างสมบูรณ์และไม่สูญเสียประสิทธิภาพการทดสอบมากนักตั้งแต่ไฟล์ ของปลอม MongoDB กำลังทำงานในหน่วยความจำ
นอกจากนี้ยังช่วยให้เราสามารถใช้แนวทางการทดสอบหน่วยที่ดีที่สุดสำหรับการทดสอบการรวม:
เป็นโบนัสเราสามารถเรียกใช้แอปพลิเคชันตัวเองกับหน่วย mongo ได้ ช่วยให้เราทำการทดสอบแบบ end-to-end สำหรับแอปพลิเคชันของเรากับฐานข้อมูลจำลอง
สำหรับการทดสอบแบบ end-to-end เราจะใช้ Selenium WebDriver และ นักวิ่งทดสอบ Hermione E2E .
ขั้นแรกเราจะบูตคนขับและนักวิ่งทดสอบ:
const mongoUnit = require('mongo-unit') const selenium = require('selenium-standalone') const Hermione = require('hermione') const hermione = new Hermione('./e2e/hermione.conf.js') //hermione config seleniumInstall() //make sure selenium is installed .then(seleniumStart) //start selenium web driver .then(mongoUnit.start) // start mongo unit .then(testMongoUrl => { process.env.MONGO_URL = testMongoUrl //store mongo url }) .then(() => { require('./index.js') //start application }) .then(delay(1000)) // wait a second till application is started .then(() => hermione.run('', hermioneOpts)) // run hermiona e2e tests .then(() => process.exit(0)) .catch(() => process.exit(1))
นอกจากนี้เรายังต้องการฟังก์ชั่นตัวช่วยบางอย่าง (ลบการจัดการข้อผิดพลาดเพื่อความกะทัดรัด):
function seleniumInstall() { return new Promise(resolve => selenium.install({}, resolve)) } function seleniumStart() { return new Promise(resolve => selenium.start(resolve)) } function delay(timeout) { return new Promise(resolve => setTimeout(resolve, timeout)) }
หลังจากกรอกฐานข้อมูลด้วยข้อมูลบางส่วนและทำความสะอาดเมื่อการทดสอบเสร็จสิ้นเราสามารถเรียกใช้การทดสอบแรกของเรา:
const expect = require('chai').expect const co = require('co') const mongoUnit = require('../index') const testMongoUrl = process.env.MONGO_URL const DATA = require('./fixtures/testData.json') const ui = { task: '.task', remove: '.task .remove', name: '#name', date: '#date', addTask: '#addTask' } describe('Tasks', () => { beforeEach(function () { return mongoUnit.initDb(testMongoUrl, DATA) .then(() => this.browser.url('http://localhost:3000')) }) afterEach(() => mongoUnit.dropDb(testMongoUrl)) it('should display list of tasks', function () { const browser = this.browser return co(function* () { const tasks = yield browser.elements(ui.task) expect(tasks.length, 1) }) }) it('should create task', function () { const browser = this.browser return co(function* () { yield browser.element(ui.name).setValue('test') yield browser.element(ui.addTask).click() const tasks = yield browser.elements(ui.task) expect(tasks.length, 2) }) }) it('should remove task', function () { const browser = this.browser return co(function* () { yield browser.element(ui.remove).click() const tasks = yield browser.elements(ui.task) expect(tasks.length, 0) }) }) })
อย่างที่คุณเห็นการทดสอบ end-to-end มีลักษณะคล้ายกับการทดสอบบูรณาการมาก
การผสานรวมและการทดสอบแบบ end-to-end มีความสำคัญสำหรับแอปพลิเคชันขนาดใหญ่ใด ๆ โดยเฉพาะแอปพลิเคชัน Node.js จะได้รับประโยชน์อย่างมากจากการทดสอบอัตโนมัติ ด้วยหน่วย mongo คุณสามารถเขียนการผสานรวมและการทดสอบแบบ end-to-end โดยไม่ต้องกังวลกับความท้าทายทั้งหมดที่มาพร้อมกับการทดสอบดังกล่าว
คุณสามารถหา ตัวอย่างที่สมบูรณ์ วิธีใช้ mongo-unit บน GitHub
การทดสอบการรวมคือการทดสอบอัตโนมัติที่ใช้เพื่อตรวจสอบว่าส่วนประกอบหลายส่วนของระบบทำงานอย่างถูกต้องสำหรับกรณีต่างๆหรือไม่
E2E ย่อมาจาก end-to-end และโดยทั่วไปจะใช้ในบริบทของการทดสอบ end-to-end