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


推荐阅读
  • 本文将介绍如何编写一些有趣的VBScript脚本,这些脚本可以在朋友之间进行无害的恶作剧。通过简单的代码示例,帮助您了解VBScript的基本语法和功能。 ... [详细]
  • Explore a common issue encountered when implementing an OAuth 1.0a API, specifically the inability to encode null objects and how to resolve it. ... [详细]
  • 本文探讨了Hive中内部表和外部表的区别及其在HDFS上的路径映射,详细解释了两者的创建、加载及删除操作,并提供了查看表详细信息的方法。通过对比这两种表类型,帮助读者理解如何更好地管理和保护数据。 ... [详细]
  • 本文详细介绍了如何在Linux系统上安装和配置Smokeping,以实现对网络链路质量的实时监控。通过详细的步骤和必要的依赖包安装,确保用户能够顺利完成部署并优化其网络性能监控。 ... [详细]
  • 本文详细介绍了如何使用 Yii2 的 GridView 组件在列表页面实现数据的直接编辑功能。通过具体的代码示例和步骤,帮助开发者快速掌握这一实用技巧。 ... [详细]
  • 深入解析Spring Cloud Ribbon负载均衡机制
    本文详细介绍了Spring Cloud中的Ribbon组件如何实现服务调用的负载均衡。通过分析其工作原理、源码结构及配置方式,帮助读者理解Ribbon在分布式系统中的重要作用。 ... [详细]
  • 本文详细介绍了Akka中的BackoffSupervisor机制,探讨其在处理持久化失败和Actor重启时的应用。通过具体示例,展示了如何配置和使用BackoffSupervisor以实现更细粒度的异常处理。 ... [详细]
  • 本文详细介绍了Java编程语言中的核心概念和常见面试问题,包括集合类、数据结构、线程处理、Java虚拟机(JVM)、HTTP协议以及Git操作等方面的内容。通过深入分析每个主题,帮助读者更好地理解Java的关键特性和最佳实践。 ... [详细]
  • 本文探讨了如何在给定整数N的情况下,找到两个不同的整数a和b,使得它们的和最大,并且满足特定的数学条件。 ... [详细]
  • 2023年京东Android面试真题解析与经验分享
    本文由一位拥有6年Android开发经验的工程师撰写,详细解析了京东面试中常见的技术问题。涵盖引用传递、Handler机制、ListView优化、多线程控制及ANR处理等核心知识点。 ... [详细]
  • 从 .NET 转 Java 的自学之路:IO 流基础篇
    本文详细介绍了 Java 中的 IO 流,包括字节流和字符流的基本概念及其操作方式。探讨了如何处理不同类型的文件数据,并结合编码机制确保字符数据的正确读写。同时,文中还涵盖了装饰设计模式的应用,以及多种常见的 IO 操作实例。 ... [详细]
  • 本文介绍了如何通过 Maven 依赖引入 SQLiteJDBC 和 HikariCP 包,从而在 Java 应用中高效地连接和操作 SQLite 数据库。文章提供了详细的代码示例,并解释了每个步骤的实现细节。 ... [详细]
  • 解决PHP与MySQL连接时出现500错误的方法
    本文详细探讨了当使用PHP连接MySQL数据库时遇到500内部服务器错误的多种解决方案,提供了详尽的操作步骤和专业建议。无论是初学者还是有经验的开发者,都能从中受益。 ... [详细]
  • 深入解析:手把手教你构建决策树算法
    本文详细介绍了机器学习中广泛应用的决策树算法,通过天气数据集的实例演示了ID3和CART算法的手动推导过程。文章长度约2000字,建议阅读时间5分钟。 ... [详细]
  • 本文详细解析了Python中的os和sys模块,介绍了它们的功能、常用方法及其在实际编程中的应用。 ... [详细]
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社区 版权所有