項目以 mocha + chai + supertest 測試驅動開發,閱讀者須要儲備的知識有:javascript
一、mocha做爲測試框架在項目中的運用 mochajs.orghtml
二、chai斷言庫的api使用 www.chaijs.comjava
三、使用supertest驅動服務器的啓動,並模擬訪問服務器地址。npm.taobao.org/package/sup…node
四、node http服務端和客戶端的基本知識 nodejs.cn/api/http.ht…express
本系列項目和文章的目標是一步一步實現一個簡化版的express,這就須要將express的源碼進行一步一步的剝離。迭代一的目標是實現服務器的啓動、app 對象get方法的簡化版本以及項目基本結構的肯定。對於get方法只實現到經過path找到對應的callback。path不作分解和匹配npm
express1
|
|-- lib
| |
| |-- express.js //負責實例化application對象
| |-- application.js //包裹app層
|
|-- examples
| |-- index.js // express1 實現的使用例子
|
|-- test
| |
| |-- index.js // 自動測試examples的正確性
|
|-- index.js //框架入口
|-- package.json // node配置文件
複製代碼
test/index.js 主要是集成mocha + chai + supertest 自動發送examples/index.js中註冊的get請求。json
examples/index.js 主要是對lib文件下的兩個源碼功能實現的驗證。設計模式
lib/express.js 對application中的對象進行初始化,完成createServer方法的callback。api
lib/application.js 一期迭代的主要功能和實現。主要實現了listen接口和get兩個對外接口和handle供express.js 使用數組
首先看看lib/application.js,代碼中有_init, _defaultConfiguration, _set, handle, listen, get幾個方法:
_init: 初始化app對象須要的一些基礎設置
_defaultConfiguration: 設置環境變量env,後期迭代預留
_set: 對app中setting對象的操做,爲後期迭代預留
handle: http.createServer 中的回調函數最終執行,遍歷paths,肯定調用哪一個get函數中的回調函數
listen: 啓動http服務。靈活使用arguments將http服務中listen方法的參數留給用戶自行配置。同時createServer方法傳入this,將在express.js中定義定app方法做爲服務請求的回調函數。
get: 實現app的get接口,主要是對全部的get請求進行註冊,存入app對象的paths數組中,方便handle中實現精準回調。
源碼:
'use strict'
/** * 採用的是設計模式中的模塊模式,定義app對象,爲其掛載方法 */
const http = require('http')
let app = exports = module.exports = {}
/** * 初始化app對象須要的一些基礎設置 * paths: 存放全部使用get方法註冊的請求,單體對象的格式爲: * { * pathURL 請求的地址 cb 請求對應的回調函數 * } */
app._init = function init() {
this.setting = {}
this.paths = []
this.defaultConfiguration()
}
/** * 設置環境變量env,後期迭代預留 */
app._defaultConfiguration = function defaultConfiguration() {
let env = process.env.NODE_ENV || 'development'
this.set('env', env)
this.set('jsonp callback name', 'callback')
}
/** * 對app中setting對象的操做,爲後期迭代預留 */
app._set = function set(key, val) {
if (arguments.length === 1) {
this.setting[key]
}
this.setting[key] = val
}
/** * http.createServer 中的回調函數最終執行,遍歷paths,肯定調用哪一個get函數中的回調函數 */
app.handle = function handle(req, res) {
let pathURL = req.url
for (let path of this.paths) {
if (pathURL === path.pathURL) {
path.cb(req, res)
}
}
}
/** * 啓動http服務 */
app.listen = function listen() {
let server = http.createServer(this)
return server
.listen
.apply(server, arguments)
}
/** * 實現app的get接口,主要是對全部的get請求進行註冊,方便handle中實現精準回調 */
app.get = function get(path, cb) {
let pathObj = {
pathURL: path,
cb: cb
}
this
.paths
.push(pathObj)
}
複製代碼
exammple/index.js 啓動服務,若是根據訪問地址的不一樣,給出不一樣的輸出
const express = require('../index.js')
const app = express()
app.listen(3000) // 啓動端口爲3000的服務
// localhost:3000/path 時調用
app.get('/path', function (req, res) {
console.log('visite /path , send : path')
res.end('path')
})
// localhost:3000/ 時調用
app.get('/', function (req, res) {
console.log('visite /, send: root')
res.end('root')
})
exports = module.exports = app
複製代碼
test/index.js 測試exapmles中的代碼,驗證是否按照地址的不一樣,進了不一樣的回調函數
'use strict'
const assert = require('chai').assert
const app = require('../examples/index.js')
const request = require('supertest')(app)
describe('服務器測試', () => {
// 若是走的不是examples中的get:/ 測試不經過
it('GET /', (done) => {
request
.get('/')
.expect(200)
.end((err, res) => {
if (err)
return done(err)
assert.equal(res.text, 'root', 'res is wrong') // 根據response調用end方法時的輸出爲: root
done()
})
})
// 若是走的不是examples中的get:/path 測試不經過
it('GET /path', (done) => {
request
.get('/path')
.expect(200)
.end((err, res) => {
if (err)
return done(err)
assert.equal(res.text, 'path', 'res is wrong') // 根據response調用end方法時的輸出爲: path
done()
})
})
})
複製代碼
test測試結果以下:
先作個簡單的嘗試,下一期咱們實現app的get,post等方法,主要是http中的methods,以及簡單的路由處理。