热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

深入解析should.js源代码及其学习路径

为了深入了解某些测试框架的工作原理,并在培训中构建一个简单的测试框架,我系统地研究了should.js的源代码。本文将分享我的学习过程和分析结果,帮助读者更好地掌握should.js的核心机制。
背景

为了研讨与进修某些测试框架的事情道理,同时也为了完成培训中完成一个简朴的测试框架的缘由,我对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.js

var 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.js

function 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中抛出来的毛病,同时在个中定义了一些信息存储的函数比方messagedetail等,能够让毛病在被捕捉的时刻带上一些特定的信息从而轻易举行推断与处置惩罚。因为完成较为简朴,因而在此就不贴出代码,须要相识的人能够自身去查阅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的完成体式格局有哪些值得自创的处所?

  1. should.js中,经由历程将一些语义词增加为属性值并返回Assertion对象自身,因而有用处理了链式挪用的题目。

  2. 经由历程Asseriton对象的属性来举行参数的通报,而不是经由历程函数参数,从而有用避免了函数挪用时参数的通报题目以及多层挪用时组织的庞杂。

  3. should.js经由历程扩大的体式格局来增加其推断的函数,保证了优越的扩大性,避免了代码耦合在一起,经由历程也为其他人编写更多的扩大代码供应了接口。

  4. should.js经由历程extend要领,让should(obj)obj.should两种体式格局到达了雷同的结果。经由历程在defineProperty中定义should属性而且在回调函数顶用should(obj)的体式格局来猎取obj对象。

  5. 经由历程抛出毛病而不是返回布尔值的体式格局来关照用户,能够越发显著的关照用户,也轻易向上抛出非常举行通报。

总结

总的来说,should.js是一个比较小而精的测试框架,他能够满足在开辟历程中所须要的大部分测试场景,同时也支撑自身编写扩大来强化它的功用。在设想上,这个框架运用了不少奇妙的要领,避免了一些庞杂的链式挪用与参数通报等题目,而且组织清楚,比较合适举行浏览与进修。


推荐阅读
  • 本文探讨了如何利用HTML5和JavaScript在浏览器中进行本地文件的读取和写入操作,并介绍了获取本地文件路径的方法。HTML5提供了一系列API,使得这些操作变得更加简便和安全。 ... [详细]
  • 深入理解Vue.js:从入门到精通
    本文详细介绍了Vue.js的基础知识、安装方法、核心概念及实战案例,帮助开发者全面掌握这一流行的前端框架。 ... [详细]
  • 在编译BSP包过程中,遇到了一个与 'gets' 函数相关的编译错误。该问题通常发生在较新的编译环境中,由于 'gets' 函数已被弃用并视为安全漏洞。本文将详细介绍如何通过修改源代码和配置文件来解决这一问题。 ... [详细]
  • 本文详细介绍了如何在云服务器上配置Nginx、Tomcat、JDK和MySQL。涵盖从下载、安装到配置的完整步骤,帮助读者快速搭建Java Web开发环境。 ... [详细]
  • 本文介绍了在Java环境中使用PDFBox和XPDF工具从PDF文件中提取文本内容的方法。重点讨论了处理中文字符集及解决相关错误的技术细节,特别是针对某些特定格式的PDF文件(如网上填写的报名表和下载的论文)遇到的问题及解决方案。 ... [详细]
  • 实用正则表达式有哪些
    小编给大家分享一下实用正则表达式有哪些,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下 ... [详细]
  • 简化报表生成:EasyReport工具的全面解析
    本文详细介绍了EasyReport,一个易于使用的开源Web报表工具。该工具支持Hadoop、HBase及多种关系型数据库,能够将SQL查询结果转换为HTML表格,并提供Excel导出、图表显示和表头冻结等功能。 ... [详细]
  • 云函数与数据库API实现增删查改的对比
    本文将深入探讨使用云函数和数据库API实现数据操作(增删查改)的不同方法,通过详细的代码示例帮助读者更好地理解和掌握这些技术。文章不仅提供代码实现,还解释了每种方法的特点和适用场景。 ... [详细]
  • 本文介绍如何从字符串中移除大写、小写、特殊、数字和非数字字符,并提供了多种编程语言的实现示例。 ... [详细]
  • 本文详细介绍如何使用 HTML5 和 JavaScript 实现一个交互式的画板功能。通过具体代码示例,帮助读者理解 Canvas API 的基本用法及其在绘图应用中的实际应用。 ... [详细]
  • 使用PHP实现网站访客计数器的完整指南
    本文详细介绍了如何利用PHP构建一个简易的网站访客统计系统。通过具体的代码示例和详细的解释,帮助开发者理解和实现这一功能,适用于初学者和有一定经验的开发人员。 ... [详细]
  • 本文详细介绍了一种通过MySQL弱口令漏洞在Windows操作系统上获取SYSTEM权限的方法。该方法涉及使用自定义UDF DLL文件来执行任意命令,从而实现对远程服务器的完全控制。 ... [详细]
  • 本文详细介绍了如何在Kendo UI for jQuery的数据管理组件中,将行标题字段呈现为锚点(即可点击链接),帮助开发人员更高效地实现这一功能。通过具体的代码示例和解释,即使是新手也能轻松掌握。 ... [详细]
  • 在尝试使用C# Windows Forms客户端通过SignalR连接到ASP.NET服务器时,遇到了内部服务器错误(500)。本文将详细探讨问题的原因及解决方案。 ... [详细]
  • Spring Boot 中静态资源映射详解
    本文深入探讨了 Spring Boot 如何简化 Web 应用中的静态资源管理,包括默认的静态资源映射规则、WebJars 的使用以及静态首页的处理方法。通过本文,您将了解如何高效地管理和引用静态资源。 ... [详细]
author-avatar
DREAM2502930781
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有