Nodejs學(xué)習(xí)筆記之測試驅(qū)動(dòng)
分享第二章,關(guān)于測試驅(qū)動(dòng)。這里的測試主要針對(duì)Web后端的測試 —— 你為什么要寫測試用例(即測試用例的完善是否是浪費(fèi)時(shí)間),如何完善你的測試用例,代碼設(shè)計(jì)如何簡化測試用例的書寫,以及一些后期的構(gòu)想。
1. 你為什么要寫測試用例
這個(gè)習(xí)慣通常會(huì)被認(rèn)為是一種耽誤開發(fā)進(jìn)度的行為,你需要花費(fèi)幾乎和開發(fā)代碼相同的時(shí)間來逐步完善你的測試用例。但是在開發(fā)過程中,在開發(fā)完成一段代碼后如果負(fù)責(zé)任而不是說完全把問題交給測試人員去發(fā)現(xiàn)的話,這個(gè)時(shí)候通常都會(huì)去做一些手動(dòng)的測試。例如:
在代碼中執(zhí)行某些方法,查看輸出的值是否符合預(yù)期。
修改數(shù)據(jù)庫/緩存,然后執(zhí)行某些方法,看數(shù)據(jù)庫的變化是否符合預(yù)期。
使用工具模擬請(qǐng)求某些接口,查看接口的返回值/數(shù)據(jù)庫的變化值是否會(huì)符合預(yù)期。
如果有前端頁面的話,還會(huì)涉及到前后端聯(lián)調(diào),即要在前端頁面上通過前端交互,查看前端的反饋是否符合預(yù)期,來間接驗(yàn)證后端代碼的正確性。
現(xiàn)代化的測試工具都在盡可能的將這些人工的手動(dòng)測試行為抽象成代碼塊,當(dāng)你有意識(shí)去進(jìn)行手動(dòng)測試的時(shí)候,其實(shí)已經(jīng)開始在嘗試測試用例的行為了。既然可以通過手動(dòng)的方式進(jìn)行測試,那為什么還需要用代碼來實(shí)現(xiàn)測試?
代碼是可以復(fù)用或者在簡單重構(gòu)后可以實(shí)現(xiàn)更多的功能的,但是當(dāng)你選擇手動(dòng)的時(shí)候,每次你都需要重頭開始。
成熟的工作流中應(yīng)當(dāng)包括代碼審核流程,代碼審核的方式有很多,逐句閱讀你的代碼,或者檢查你測試代碼的完善性以及正確性,然后運(yùn)行你的測試用例。后者更加簡單。
當(dāng)代碼改動(dòng),例如修復(fù) Bug 時(shí)候,很難保證你的改動(dòng)是否會(huì)影響其他依賴你代碼的部分。在人工測試的時(shí)代有一個(gè)叫做回歸測試,即在你修復(fù) Bug 將你的系統(tǒng)重新測試一遍。但是如果你已經(jīng)有了完善的測試用例了呢,直接執(zhí)行命令搞定。
當(dāng)你重構(gòu)代碼的時(shí)候,同上。
2. 如何完善你的測試用例
在進(jìn)入完善階段前,先說說你將如何實(shí)現(xiàn)測試用例。
describe Meme do before do @meme = Meme.new end describe "when asked about cheeseburgers" do it "must respond positively" do @meme.i_can_has_cheezburger?.must_equal "OHAI!" end end describe "when asked about blending possibilities" do it "won't say no" do @meme.will_it_blend?.wont_match /^no/i end end end
上面的代碼來自于 Ruby 的 minitest。before 包含的代碼塊是在執(zhí)行下面的測試用例前要做的事情,通常還會(huì)支持一個(gè)相對(duì)應(yīng)的方法,在測試用例執(zhí)行完執(zhí)行。每個(gè)用例里面都進(jìn)行一些很小的判斷。
第一段中提到了一些手動(dòng)測試?yán)锩娼?jīng)常會(huì)涉及到的測試內(nèi)容,這里拿其中的 2 和 3 進(jìn)行說明。在進(jìn)行數(shù)據(jù)庫相關(guān)的測試時(shí),需要在 before 中插入一條測試數(shù)據(jù),并且在 after 中刪除測試數(shù)據(jù)。中間的測試用例中,通過執(zhí)行相應(yīng)的方法,執(zhí)行完畢后:檢查數(shù)據(jù)變化情況/檢查是否有預(yù)期的異常/是否返回預(yù)期結(jié)果 來確認(rèn)代碼的正確性。如果是接口的話,就是通過代碼發(fā)起對(duì)應(yīng)的請(qǐng)求,然后檢查返回的內(nèi)容是否返回預(yù)期,有需要的話再去查看數(shù)據(jù)庫里面的數(shù)據(jù)是否符合預(yù)期變化。
現(xiàn)在已經(jīng)有了測試用例,但是任然需要考慮一種特殊情況。我現(xiàn)在為一個(gè)函數(shù)寫了相對(duì)完善的測試用例了,跑完都 PASS 了,結(jié)果發(fā)現(xiàn)線上的日志里面還是有那個(gè)函數(shù)的報(bào)錯(cuò)。檢查下發(fā)現(xiàn)函數(shù)的某個(gè)分支之前在測試的時(shí)候沒有測試到,剛好線上的某種情況運(yùn)行到了這個(gè)分支,結(jié)果有一個(gè)很不明顯的語法錯(cuò)誤報(bào)錯(cuò)了,有沒有辦法能確保所有的代碼都測試過了?這里需要引入的是一個(gè)叫做 測試用例覆蓋率 的概念,基本上每個(gè)語言都會(huì)有響應(yīng)的實(shí)現(xiàn)。通過測試用例覆蓋率,量化的告訴你你的測試用例有沒有跑完某某文件里的所有代碼,而你需要做的,就是盡可能保證你的覆蓋率保持在 100%。
某種意義上來說,測試用例和測試覆蓋率是用來提高開發(fā)者對(duì)自己代碼自信心的工具。但是,他們也不是萬能的。測試用例里面總可能會(huì)漏掉一些參數(shù)的可能性,當(dāng)然你的代碼里面也沒有為這種可能性進(jìn)行代碼的編寫,最終測試用例覆蓋率只能告訴你你寫的代碼我們都幫你檢測過了測試過了,對(duì)于你沒有考慮到的可能性,表示無能為力。所以盡可能編寫嚴(yán)格的代碼,例如 javascript 里面盡可能都用 === 而不是 ==,使用強(qiáng)類型的編程規(guī)范等等,這些來降低這種因?yàn)榻邮艿膮?shù)范圍過大帶來的潛在風(fēng)險(xiǎn)。
3. 代碼設(shè)計(jì)如何簡化測試用例的書寫
整個(gè) Web (也不局限于 web)通常包括三個(gè)層面的代碼 —— 單純數(shù)據(jù)處理與運(yùn)算、涉及到數(shù)據(jù)庫、涉及到具體的網(wǎng)絡(luò)協(xié)議。其中單純的數(shù)據(jù)運(yùn)算于處理主要為普通的運(yùn)算的函數(shù)或者是其他代碼,涉及到數(shù)據(jù)庫就是傳統(tǒng)意義上 MVC 里面的 M,涉及到具體的網(wǎng)絡(luò)協(xié)議就是對(duì)應(yīng)的 C。這三塊的測試分別對(duì)應(yīng)著第一節(jié)中常規(guī)的測試內(nèi)容的前三條。
因?yàn)?C層面通常還可能涉及到頁面的渲染以及相應(yīng)協(xié)議的模擬,所以通常把測試的重心放在函數(shù)以及數(shù)據(jù)庫相關(guān)的代碼里面可以減少測試用例代碼的復(fù)雜度,這個(gè)就要求 Controller 的代碼要盡可能少。對(duì)于復(fù)雜度較高的應(yīng)用的一些目前的一些建議:
將數(shù)據(jù)的基礎(chǔ)校驗(yàn)都放在 M層,如果使用 Ruby 開發(fā)的話,ActiveRecord以及Mongoid都提供了很方便使用的 validation 功能。
嘗試在代碼中使用 Pub/Sub 模式配合一些 ORM中提供的鉤子(hook) 來實(shí)現(xiàn) Model 之間的通信。 例如在 A 創(chuàng)建的時(shí)候發(fā)布某個(gè)消息,B監(jiān)聽到消息之后修改他自己的某個(gè)屬性值。
使用 Command 模式將一些業(yè)務(wù)無關(guān)的功能從系統(tǒng)中抽離出來,例如郵件發(fā)送。
以上建議參考:Laravel wisper resque
4. 構(gòu)想
以上的內(nèi)容都避開了前后端需要聯(lián)調(diào)的測試用例,下面的內(nèi)容主要是針對(duì)這塊。Ruby 在這個(gè)方向已經(jīng)有一些比較優(yōu)雅的實(shí)現(xiàn),感興趣的可以直接先去欣賞一下 Capybara。
隨著包括 Selenium Phantomjs 以及基于前者的 Watir 等一系列瀏覽器驅(qū)動(dòng)的普及,使用代碼控制瀏覽器已經(jīng)不再是一件很復(fù)雜的事情。在這個(gè)能力的基礎(chǔ)上,可以嘗試把基于前端的測試分為四步:
等待某標(biāo)志性元素出現(xiàn)(例如等待頁面載入玩,或者某個(gè)內(nèi)容異步加載出現(xiàn))
模擬用戶操作,這里的操作包括且不局限于用戶點(diǎn)擊、用戶輸入
等待反饋中標(biāo)志性元素出現(xiàn)(例如某某輸入框出現(xiàn))
判斷內(nèi)容,是否符合預(yù)期
基于這個(gè)流程,可以解決絕大多數(shù)的前端測試。但是單純依靠這個(gè)流程任然不夠,因?yàn)轫撁嬷锌赡艹霈F(xiàn)例如驗(yàn)證碼這樣的阻礙元素,在不修改代碼的前提下,可以嘗試通過數(shù)據(jù)庫/緩存來取到這些內(nèi)容。同樣,和測試接口相同,這里也涉及到在測試前數(shù)據(jù)庫中插入測試數(shù)據(jù),測試用例執(zhí)行后嚴(yán)重?cái)?shù)據(jù)庫里面數(shù)據(jù)變化,以及全部測試完畢后刪除測試數(shù)據(jù)的內(nèi)容。最終導(dǎo)致這塊測試用例代碼的實(shí)現(xiàn)需要同時(shí)對(duì)前端后端有一定的了解。目前還在考慮在借鑒 Capybara 的基礎(chǔ)上,設(shè)計(jì)出更加通用的方案。
最后貼一段 Capybara 的代碼結(jié)束這段內(nèi)容:
feature "Signing in" do background do User.make(:email => 'user@example.com', :password => 'caplin') end scenario "Signing in with correct credentials" do visit '/sessions/new' within("#session") do fill_in 'Email', :with => 'user@example.com' fill_in 'Password', :with => 'caplin' end click_button 'Sign in' expect(page).to have_content 'Success' end given(:other_user) { User.make(:email => 'other@example.com', :password => 'rous') } scenario "Signing in as another user" do visit '/sessions/new' within("#session") do fill_in 'Email', :with => other_user.email fill_in 'Password', :with => other_user.password end click_button 'Sign in' expect(page).to have_content 'Invalid email or password' end end
相關(guān)文章
使用?Node-RED對(duì)?MQTT?數(shù)據(jù)流處理
本文將介紹使用 Node-RED 連接到 MQTT 服務(wù)器,并對(duì) MQTT 數(shù)據(jù)進(jìn)行過濾和處理后再將其發(fā)送至 MQTT 服務(wù)器的完整操作流程。讀者可以快速了解如何使用 Node-RED 對(duì) MQTT 數(shù)據(jù)進(jìn)行簡單的流處理2022-05-05node自定義安裝更改npm全局模塊默認(rèn)安裝路徑的步驟
有段時(shí)間沒用npm了,新建個(gè)項(xiàng)目,需要改變npm全局包默認(rèn)安裝的路徑,本文就來介紹一下node自定義安裝更改npm全局模塊默認(rèn)安裝路徑的步驟,感興趣的可以了解下2021-09-09詳解如何使用koa實(shí)現(xiàn)socket.io官網(wǎng)的例子
這篇文章主要介紹了詳解如何使用koa實(shí)現(xiàn)socket.io官網(wǎng)的例子,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-11-11pm2發(fā)布node配置文件ecosystem.json詳解
這篇文章主要介紹了pm2發(fā)布node配置文件ecosystem.json詳解,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-05-05Node.js設(shè)置定時(shí)任務(wù)之node-schedule模塊的使用詳解
node-schedule是 Node.js 的一個(gè)定時(shí)任務(wù)(crontab)模塊。這篇文章主要介紹了Node.js設(shè)置定時(shí)任務(wù)之node-schedule模塊的使用,需要的朋友可以參考下2020-04-04nodejs對(duì)mongodb數(shù)據(jù)庫的增加修刪該查實(shí)例代碼
在本篇文章里小編給大家整理的是一篇關(guān)于nodejs對(duì)mongodb數(shù)據(jù)庫的增加修刪該查實(shí)例代碼,有需要的朋友們可以參考下。2020-01-01