摘要:結構其中為整個項目入口,為中的類,負責對測試信息進行記錄。通過拋出錯誤而不是返回布爾值的方式來通知用戶,能夠更加明顯的通知用戶,也方便向上拋出異常進行傳遞。
背景
為了研究與學習某些測試框架的工作原理,同時也為了完成培訓中實現一個簡單的測試框架的原因,我對should.js的代碼進行了學習與分析,現在與大家來進行交流下。
目錄ext
assertion.js
assertion-error.js
config.js
should.js
util.js
其中ext為文件夾,其余為js文件。
結構其中should.js為整個項目入口,asssertion.js為should.js中的類,負責對測試信息進行記錄。assertion-error.js為should.js定義了一個錯誤類,負責存儲錯誤信息。config.js中存儲了一些should.js中的一些配置信息。util.js中則定義了一些項目中常用的工具函數。
should.jsvar should = function should(obj) { return (new should.Assertion(obj)); }; should.AssertionError = require("./assertion-error"); should.Assertion = require("./assertion"); should.format = util.format; should.type = require("should-type"); should.util = util; should.config = require("./config"); exports = module.exports = should;
should.js入口文件初始化了一個類,并將所有文件中其他的模塊進行引入。同時將自己export出去,讓自己能夠被require到。
should.extend = function (propertyName, proto) { propertyName = propertyName || "should"; proto = proto || Object.prototype; var prevDescriptor = Object.getOwnPropertyDescriptor(proto, propertyName); Object.defineProperty(proto, propertyName, { set: function () { }, get: function () { return should(util.isWrapperType(this) ? this.valueOf() : this); }, configurable: true }); return { name: propertyName, descriptor: prevDescriptor, proto: proto}; };
should.js自身定義了一個extend方法,用于兼容should.js的另一種調用方式,即should(obj)的方式等于should.js的常規調用方式obj.should,從而兼容另一種寫法。
should .use(require("./ext/assert")) .use(require("./ext/chain")) .use(require("./ext/bool")) .use(require("./ext/number")) .use(require("./ext/eql")) .use(require("./ext/type")) .use(require("./ext/string")) .use(require("./ext/property")) .use(require("./ext/error")) .use(require("./ext/match")) .use(require("./ext/contain"));
should.js中還定義了use方法,從而讓我們能夠自己編寫一些類型判斷例如isNumber等函數導入到項目中,從而方便進行測試。項目目錄中的ext文件夾就是編寫的一些簡單的should.js的擴展。后面將在介紹擴展時對兩者的工作原理以及使用方法進行介紹。
assertion.jsfunction Assertion(obj) { this.obj = obj; //any標志位 //@type {boolean} this.anyOne = false; //not標志位 //@type {boolean} this.negate = false; this.params = {actual: obj}; }
assertion.js中定義了一個Assertion類,其中any為should.js中的any方法的標志位,而not則為其not方法的標志位。
Assertion.add = function(name, func) { var prop = {enumerable: true, configurable: true}; prop.value = function() { var context = new Assertion(this.obj, this, name); context.anyOne = this.anyOne; try { func.apply(context, arguments); } catch(e) { //check for fail if(e instanceof AssertionError) { //negative fail if(this.negate) { this.obj = context.obj; this.negate = false; return this; } if(context !== e.assertion) { context.params.previous = e; } //positive fail context.negate = false; context.fail(); } // throw if it is another exception throw e; } //negative pass if(this.negate) { context.negate = true;//because .fail will set negate context.params.details = "false negative fail"; context.fail(); } //positive pass if(!this.params.operator) this.params = context.params;//shortcut this.obj = context.obj; this.negate = false; return this; }; Object.defineProperty(Assertion.prototype, name, prop); };
assertion.js中的add方法在Assertion的原型鏈中添加自定義命名的方法,從而讓我們能夠打包一些判斷的方法來進行調用,不需要重復進行代碼的編寫。該方法具體的使用方式我們在后面對擴展進行講解時將會提到。
Assertion.addChain = function(name, onCall) { onCall = onCall || function() { }; Object.defineProperty(Assertion.prototype, name, { get: function() { onCall(); return this; }, enumerable: true }); };
addChain方法添加屬性到原型鏈中,該屬性在調用方法后返回調用者本身。該方法在should.js的鏈式調用中起著重要的作用。
同時,Assertion類還支持別名功能,alias方法使用Object對象的getOwnPropertyDescriptor方法來對屬性是否存在進行判斷,并調用defineProperty進行賦值。
Assertion類在原型鏈中定義了assert方法,用來對各級限制條件進行判斷。assert方法與普通方法不同,它并未采用參數來進行一些參數的傳遞,而是通過assert方法所在的Assertion對象的params屬性來進行參數的傳遞。因為在Assertion對象中存儲了相關的信息,使用這個方法來進行參數傳遞方便在各級中assert函數的調用方便。具體使用方法我們將在擴展的分析時提到。
assert: function(expr) { if(expr) return this; var params = this.params; if("obj" in params && !("actual" in params)) { params.actual = params.obj; } else if(!("obj" in params) && !("actual" in params)) { params.actual = this.obj; } params.stackStartFunction = params.stackStartFunction || this.assert; params.negate = this.negate; params.assertion = this; throw new AssertionError(params); }
Assertion類也定義了一個fail方法能夠讓用戶直接調用從而拋出一個Assertion的Error。
fail: function() { return this.assert(false); }assertion-error.js
在此文件中,定義了assertion中拋出來的錯誤,同時在其中定義了一些信息存儲的函數例如message和detail等,能夠讓錯誤在被捕獲的時候帶上一些特定的信息從而方便進行判斷與處理。由于實現較為簡單,因此在此就不貼出代碼,需要了解的人可以自己去查閱should.js的源碼。
ext/bool.js下面簡單介紹一個Assertion的擴展的工作方式。讓我們能夠對should.js的工作原理有一個更加深刻的理解。
module.exports = function(should, Assertion) { Assertion.add("true", function() { this.is.exactly(true); }); Assertion.alias("true", "True"); Assertion.add("false", function() { this.is.exactly(false); }); Assertion.alias("false", "False"); Assertion.add("ok", function() { this.params = {operator: "to be truthy"}; this.assert(this.obj); }); }; //should.js should.use = function (f) { f(should, should.Assertion); return this; }; //use "1"should.be.true();
通過上面的擴展模塊代碼以及should.js文件中的use函數,我們可以發現,use函數向擴展模塊傳入了should方法和Assertion構造函數。在bool.js這個擴展模塊中,它通過調用Assertion對象上的add函數來添加新的判斷方式,并且通過params參數來告訴Assertion對象如果判斷失敗應該如何提示用戶。
感想 should.js如何實現鏈式調用?在Assertion類中,有一個addChain方法,該方法為某些屬性定義了一些在getter函數中調用的操作方法,并且返回對象本身。通過這個方法,在ext/chain.js中,它為should.js中常見的語義詞添加了屬性,并通過返回對象本身來達到鏈式調用的Assertion對象傳遞。
["an", "of", "a", "and", "be", "has", "have", "with", "is", "which", "the", "it"].forEach(function(name) { Assertion.addChain(name); });
以下兩段代碼在結果上是一模一樣的效果:
"1".shoud.be.a.Number(); "1".should.be.be.be.be.a.a.a.a.Number();should.js的實現方式有哪些值得借鑒的地方?
should.js中,通過將一些語義詞添加為屬性值并返回Assertion對象本身,因此有效解決了鏈式調用的問題。
通過Asseriton對象的屬性來進行參數的傳遞,而不是通過函數參數,從而有效避免了函數調用時參數的傳遞問題以及多層調用時結構的復雜。
should.js通過擴展的方式來添加其判斷的函數,保證了良好的擴展性,避免了代碼耦合在一起,通過也為其他人編寫更多的擴展代碼提供了接口。
should.js通過extend方法,讓should(obj)與obj.should兩種方式達到了相同的效果。通過在defineProperty中定義should屬性并且在回調函數中用should(obj)的方式來獲取obj對象。
通過拋出錯誤而不是返回布爾值的方式來通知用戶,能夠更加明顯的通知用戶,也方便向上拋出異常進行傳遞。
總結總的來說,should.js是一個比較小而精的測試框架,他能夠滿足在開發過程中所需要的大部分測試場景,同時也支持自己編寫擴展來強化它的功能。在設計上,這個框架使用了不少巧妙的方法,避免了一些復雜的鏈式調用與參數傳遞等問題,而且結構清晰,比較適合進行閱讀與學習。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/87723.html
摘要:通過添加一個回調函數通常命名為給方法,就會知道,它應該等這個函數被調用的時候才能完成測試。此外提供了一些鉤子函數和。這些鉤子函數可以用于設置測試的先決條件或者對測試進行清理。鉤子函數會按照它們被定義的順序運行。 Mocha 的安裝和使用 1. 安裝 使用npm全局安裝: npm install -g mocha 安裝Mocha >= v3.0.0,npm的版本應該>=v1.4...
摘要:在真正寫了一段時間的基礎組件和基礎工具后,才發現自動化測試有很多好處。有了自動化測試,開發者會更加信任自己的代碼。由于維護測試用例也是一大筆開銷畢竟沒有多少測試會專門幫前端寫業務測試用例,而前端使用的流程自動化工具更是沒有測試參與了。 本文轉載自 天貓前端博客,更多精彩文章請進入天貓前端博客查看 前言 為何要測試 以前不喜歡寫測試,主要是覺得編寫和維護測試用例非常的浪費時間。在真正寫了...
摘要:感謝使用框架本文檔涵蓋構建應用所需的基礎知識。用于數據校驗的組件及相關文件在此目錄進行管理。除了自定義中間件外,還是用了諸多第三方的中間件,它們是五測試我們使用組件對服務端代碼進行測試。識別當前導航從已有導航中刪除給定標識的導航配置。 本文同步至個人博客 MEAN.js 文檔,轉載請注明出處。 Overview 感謝使用 MEAN.js 框架! 本文檔涵蓋構建 MEAN 應用所需的基礎...
閱讀 2888·2021-11-15 11:39
閱讀 1513·2021-08-19 10:56
閱讀 1093·2019-08-30 14:12
閱讀 3731·2019-08-29 17:29
閱讀 719·2019-08-29 16:21
閱讀 3417·2019-08-26 12:22
閱讀 1515·2019-08-23 16:30
閱讀 1015·2019-08-23 15:25