你是否正在使用Express框架?你是不是正在做服務(wù)端的集成測(cè)試(end-to-end)?這里我將討論在項(xiàng)目中遇到的問(wèn)題。這篇文章涵蓋了我在使用Express框架做集成測(cè)試時(shí)所遇到的挑戰(zhàn),以及我是如何尋找技術(shù)解決方案的。
當(dāng)你也遇到相同的問(wèn)題時(shí),希望這篇文章對(duì)你有所幫助。
背景
我做的項(xiàng)目是先要向amazon s3批量上傳圖片文件,然后對(duì)這些文件做一些裁剪、壓縮和編輯的任務(wù)。
使用Express中間件做認(rèn)證、授權(quán)以及設(shè)置上下文的工作都很順利。我使用cookie-session作為控制session的中間件,這就意味著需要cookie-session負(fù)責(zé)相應(yīng)session數(shù)據(jù)的加密、配置和解密。
我知道許多開(kāi)發(fā)者使用sinon for unit testing。如果你對(duì)它不熟悉,我先簡(jiǎn)短介紹下它:sinon for unit testing是通過(guò)Javascript的測(cè)試框架(如mocha)來(lái)實(shí)現(xiàn)mock的方法。但是問(wèn)題來(lái)了,如果要實(shí)現(xiàn)Express Session的mock測(cè)試?在Express中有沒(méi)有實(shí)現(xiàn)mock session的解決方案?

挑戰(zhàn)
下面我將講述我所遇到的挑戰(zhàn)。我有兩個(gè)路由,一個(gè)是向s3上傳圖片的、另一個(gè)是對(duì)上傳圖片做一些額外處理(如壓縮、編輯)。對(duì)于第一個(gè)路由我先設(shè)置請(qǐng)求的session,然后再跳轉(zhuǎn)到第二個(gè)路由。
1)api/image/create
exports.create = function(req, res) {
// upload image to s3
var photos = PhotosTable.create(req.files.names); // add entries in database
req.session.count = photos.length; // set count in req.session
res.redirect(`api/image/submit`);
}
- api/image/submit (need to add a test case for this one)
exports.submit = function(req, res) {
if (req.session.count && req.session.count > 0) { // get count from req.session
// perform further tasks
res.sendStatus(200);
} else {
res.sendStatus(400);
}
}
我想mock第二個(gè)路由(api/image/submit)。然而問(wèn)題是:(pi/image/submit)這個(gè)路由需要先核查req.session.count,然后在繼續(xù)后面的操作。如果count是空值,回應(yīng)400。使用Mocha框架你只能mock第一個(gè)路由,不能實(shí)現(xiàn)下面的功能:第一個(gè)路由跳轉(zhuǎn)到第二個(gè)路由,然后程序從第二個(gè)路由向第一個(gè)路由的返回值,然后驗(yàn)證第二個(gè)路由返回值的測(cè)試用例。
這個(gè)問(wèn)題看起來(lái)很簡(jiǎn)單,但是實(shí)現(xiàn)起來(lái)遇到很多挑戰(zhàn)
最簡(jiǎn)單的解決方案是在測(cè)試用例中,通過(guò)req params直接請(qǐng)求第二個(gè)路由,然后檢查params是否存在,如果存在就取消檢查req.session.count。
然而,這種解決方案并不是最好的。因?yàn)?/p>
- 這不是一種普通的方式,因此這種方法不能被重用
- 在代碼中添加額外的if/else來(lái)控制req.params,就會(huì)需要更多的代碼來(lái)確保原來(lái)的邏輯不受影響
我們也可以放棄使用兩個(gè)個(gè)路由,把所有的代碼都寫(xiě)在一個(gè)路由中。但是我們真的要這么做嗎?據(jù)我所知,不應(yīng)該為了測(cè)試用例更改代碼(one should never change implementatin for the sake of test cases)
這個(gè)問(wèn)題看起來(lái)很普通,但是我在網(wǎng)上(至少是在StackOverFlow)上并沒(méi)有找到解決方案。起初我猜測(cè)在一些模塊中已經(jīng)解決了這個(gè)問(wèn)題,諸如mocha, 或是cookie-seeion等經(jīng)常在Express中使用的模塊。
然而,并沒(méi)有。
我最終是如何解決的
在程序中我是使用cookie-session來(lái)維護(hù)Express與session間的管理工作。cookie-session通過(guò)Keygrip和Crypto來(lái)設(shè)置和驗(yàn)證session的簽名。
在設(shè)置session cookies時(shí)需要name和key:
name被用來(lái)作為cookie的名字。
key是作為Keygrip對(duì)cookie值進(jìn)行簽名的概要值。
我在測(cè)試用例請(qǐng)求第二個(gè)路由時(shí),設(shè)置req.session.count的值。下面就是代碼:
// name = "my-session" -> base64 value of required object (cookie)
// name.sig = "my-session.sig" -> signed value of cookie using Keygrip
let cookie = Buffer.from(JSON.stringify({"count":2})).toString('base64'); // base64 converted value of cookie
let kg = Keygrip(['testKey']) // same key as I'm using in my app
let hash = kg.sign('my-session=' + cookie);
await request(app).get('api/image/submit').set("Accept", "text/html")
.set('cookie', ['my-session=' + cookie + '; ' + 'my-session.sig=' + hash + ';'])
.expect(200);
這僅僅是一條mock session的測(cè)試用例。在大多數(shù)情況下,測(cè)試用例需要定制所要測(cè)試的參數(shù)值,但這個(gè)代碼可以設(shè)置任何你想測(cè)試的參數(shù)。
我已經(jīng)創(chuàng)建一個(gè)Nodejs.js包并發(fā)不到npm中,通過(guò)這個(gè)包mock-session,你可以很容易做mock session的測(cè)試。