如何Mock Express的會(huì)話

文章翻譯自How to Mock an Express session。

你是否正在使用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的解決方案?

Test Expressing.jpeg

挑戰(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`);
}
  1. 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è)試。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容