1

一、概述

背景

为了提高个人基础能力,学习优秀项目的设计和代码编写方式,因此学习should.js的源码并进行分析

什么是should.js


should is an expressive, readable, framework-agnostic assertion library.

should.js是一个表现力强、可读性强、与框架无关的断言库。Node.js自带的断言库Assert,10 多个断言测试的函数,但是功能有限,而should.js可以让测试更加友好,因此一般前端的单元测试会引入should.js。

二、使用方法

示例

var should = require('should');

var user = {
    name: 'tj'
  , pets: ['tobi', 'loki', 'jane', 'bandit']
};

user.should.have.property('name', 'tj');
user.should.have.property('pets').with.lengthOf(4);

// if the object was created with Object.create(null)
// then it doesn't inherit `Object` and have the `should` getter
// so you can do:

should(user).have.property('name', 'tj');
should(true).ok;

someAsyncTask(foo, function(err, result){
  should.not.exist(err);
  should.exist(result);
  result.bar.should.equal(foo);
});

使用方法


1、安装

$ npm install should --save-dev

2、使用

var should = require('should');

(5).should.be.exactly(5).and.be.a.Number;

三、项目结构介绍

项目目录


image.png

四、源码分析

index.js

项目入口文件,导出node.js

/lib/node.js


var should = require('./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/http'))
  .use(require('./ext/error'))
  .use(require('./ext/match'))
  .use(require('./ext/deprecated'));

 module.exports = should;

引入should.js,然后use方法引入ext文件夹下的扩展方法,便于在测试过程中使用引入的扩展方法。最后导出should。下面我们先介绍should。

/lib/should.js


/**
 * Our function should
 * @param obj
 * @returns {Assertion}
 */
var should = function(obj) {
  return new Assertion(util.isWrapperType(obj) ? obj.valueOf(): obj);
};

/**
 * Initialize a new `Assertion` with the given _obj_.
 *
 * @param {*} obj
 * @api private
 */

var Assertion = should.Assertion = function Assertion(obj) {
  this.obj = obj;
};

...
exports = module.exports = should;

should方法返回一个新的Assertion实例,而Assertion断言函数通过传递的obj参数初始化,并在Assertion的原型上定义assert、getMessage、copy、copyIfMissing方法。

接下来定义扩展断言函数的方法:add、alias,它使用了一些逻辑只定义肯定的断言和否定的断言本身的规则。所有的动作都发生在子文本中,这种方法考虑的是否定。我们可以添加一些不依赖于断言状态的修饰符。

通过util为should定义断言错误、format和use方法,最后将should导出,便于前端进行单元测试时通过require将其引入。其中uti文件中定义了项目中用到的一些函数,例如判断参数是否是字符串、将b对象属性复制到a对象并返回a对象的merge方法等。

/lib/ext/assert.js


将assert暴露给should,通过util文件中的merge方法将asset中的方法定义在should上,这样在使用的过程中不必使用require()的方法再将asset模块引入。

为should定义exist方法,用于判断传入的第一个参数是否是null,同时在第一个参数是null时抛出错误显示第二个参数给定的信息。should.not.exist方法则定义当传入的第一个参数不是null时报错。

/lib/ext/chain.js


module.exports = function(should, Assertion) {

  function addLink(name) {
    Object.defineProperty(Assertion.prototype, name, {
      get: function() {
        return this;
      }
    });
  }

  ['an', 'of', 'a', 'and', 'be', 'have', 'with', 'is', 'which', 'the'].forEach(addLink);
};

对'an', 'of', 'a', 'and', 'be', 'have', 'with', 'is', 'which', 'the'词语通过addLink方法定义到Assert原型上,使得Assertion也包含这些属性,并且返回Assertion对象本身,实现should的链式调用。

源码学习小结


  • should.js的文件结构设计合理,例如将should的扩展方法统一放到ext文件夹下面,并且根据功能单独存放到文件中,这样不仅结构清晰,并且模块化的方式提高代码的复用性,也增强了项目的扩展性
  • 项目通过chain.js文件中的方法实现了Assertion的链式调用

五、总结

should.js项目结构非常清晰,虽然和之前学习的axios、promise等项目相比,代码量稍多,但是根据项目结构阅读起来也不会存在特别大的难度。should.js非常语义化,感觉像是使用英文写自然语言一样写测试,上手容易并且容易阅读,推荐使用should.js进行前端单元测试。


涛八八
27 声望0 粉丝