mocha 简介

mocha 是 JavaScript 的一个单元测试框架,既可以在浏览器环境中运行,也可以在 node.js 环境下运行。我们只需要编写测试用例,mocha 会将测试自动运行并给出测试结果。

mocha 的主要特点有:

  • 既可以测试简单的 JavaScript 函数,又可以测试异步代码;  可以自动运行所有测试,也可以只运行特定的测试;
  • 可以支持 before、after、beforeEach 和 afterEach 来编写初始化代码。

测试脚本示例

假设我们编写了一个 sum.js,并且输出一个简单的求和函数:

module.exports = function(...rest) {var sum = 0;for (let n of rest) {sum += n;}return sum;
};

这个函数非常简单,就是对输入的任意参数求和并返回结果。

如果我们想对这个函数进行测试,可以写一个 test.js,然后使用 Node.js 提供的 assert 模块进行断言:

const assert = require('assert');
const sum = require('./sum');
assert.strictEqual(sum(), 0);
assert.strictEqual(sum(1), 1);
assert.strictEqual(sum(1, 2), 3);
assert.strictEqual(sum(1, 2, 3), 6);

assert 模块非常简单,它断言一个表达式为 true。如果断言失败,就抛出Error。

单独写一个 test.js 的缺点是没法自动运行测试,而且,如果第一个 assert报错,后面的测试也执行不了了。

如果有很多测试需要运行,就必须把这些测试全部组织起来,然后统一执行,并且得到执行结果。这就是我们为什么要用 mocha 来编写并运行测试。

我们利用 mocha 修改后的测试脚本如下:

const assert = require('assert');
const sum = require('../sum');
describe('#sum.js', () => {describe('#sum()', () => {it('sum() should return 0', () => {assert.strictEqual(sum(), 0);});it('sum(1) should return 1', () => {assert.strictEqual(sum(1), 1);});it('sum(1, 2) should return 3', () => {assert.strictEqual(sum(1, 2), 3);});it('sum(1, 2, 3) should return 6', () => {assert.strictEqual(sum(1, 2, 3), 6);});});
});

这里我们使用 mocha 默认的 BDD-style 的测试。describe 可以任意嵌套,以便把相关测试看成一组测试。

describe 可以任意嵌套,以便把相关测试看成一组测试;而其中的每个 it就代表一个测试。

每个 it(“name”, function() {…})就代表一个测试。例如,为了测试 sum(1, 2),我们这样写:

it('sum(1, 2) should return 3', () => {assert.strictEqual(sum(1, 2), 3);
});

编写测试的原则是,一次只测一种情况,且测试代码要非常简单。我们编写多个测试来分别测试不同的输入,并使用 assert 判断输出是否是我们所期望的。

运行测试脚本

下一步,我们就可以用 mocha 运行测试了。打开命令提示符,切换到项目目录,然后创建文件夹 test,将 test.js 放入 test 文件夹下,执行命令:

./node_modules/mocha/bin/mocha

mocha 就会自动执行 test 文件夹下所有测试,然后输出如下:

#sum.js#sum()✓ sum() should return 0✓ sum(1) should return 1✓ sum(1, 2) should return 3✓ sum(1, 2, 3) should return 64 passing (7ms)

这说明我们编写的 4 个测试全部通过。如果没有通过,要么修改测试代码,要么修改 hello.js,直到测试全部通过为止。

编写合约测试脚本

测试时我们通常会把每次测试运行的环境隔离开,以保证互不影响。对应到合约测试,我们每次测试都需要部署新的合约实例,然后针对新的实例做功能测试。 Car 合约的功能比较简单,我们只要设计 2 个测试用例:

  • 合约部署时传入的 brand 属性被正确存储;
  • 调用 setBrand 之后合约的 brand 属性被正确更新;
    新建测试文件 tests/car.spec.js,完整的测试代码如下。
const path = require('path');
const assert = require('assert');
const ganache = require('ganache-cli');
const Web3 = require('web3');
// 1. 配置 provider
const web3 = new Web3(ganache.provider());
// 2. 拿到 abi 和 bytecode
const contractPath = path.resolve(__dirname,
'../compiled/Car.json');
const { interface, bytecode } = require(contractPath);
let accounts;
let contract;
const initialBrand = 'BMW';
describe('contract', () => {// 3. 每次跑单测时需要部署全新的合约实例,起到隔离的作用beforeEach(async() => {accounts = await web3.eth.getAccounts();console.log('合约部署账户:', accounts[0]);contract = await newweb3.eth.Contract(JSON.parse(interface)).deploy({data: bytecode,arguments: [initialBrand]}).send({from: accounts[0],gas: '1000000'});console.log('合约部署成功:',contract.options.address);});// 4. 编写单元测试it('deployed contract', () => {assert.ok(contract.options.address);});it('should has initial brand', async() => {const brand = await contract.methods.brand().call();assert.equal(brand, initialBrand);});it('can change the brand', async() => {const newBrand = 'Benz';await contract.methods.setBrand(newBrand).send({from: accounts[0]});const brand = await contract.methods.brand().call();assert.equal(brand, newBrand);});
});

整个测试代码使用的断言库是 Node.js 内置的 assert 模块,assert.ok() 用于判断表达式真值,等同于 assert(),如果为 false 则抛出 error;assert.equal() 用于判断实际值和期望值是否相等(==),如果不相等则抛出 error。

beforeEach 是 mocha 里提供的声明周期方法,表示每次运行时每个 test执行前都要做的准备操作。因为我们知道,在测试前初始化资源,测试后释放资源是非常常见的,所以 mocha 提供了 before、after、beforeEach 和 afterEach来实现这些功能。

测试的关键步骤也用编号的数字做了注释,其中步骤 1、2、3 在合约部署脚本中已经比较熟悉,需要注意的是 ganache-cli provider 的创建方式。我们在脚本中引入 ganache,将模拟以太坊节点嵌入测试中,就不会影响我们外部运行的节点环境了。
测试中我们用到了 web3.js 中两个与合约实例交互的方法,之前我们已经接触过,以后在 DApp 开发时会大量使用:

  • contract.methods.brand().call(),调用合约上的方法,通常是取数据,立即返回,与 v0.20.1 版本中的 .call() 相同;
  • contract.methods.setBrand('xxx').send(),对合约发起交易,通常是修改数据,返回的是交易 Hash,相当于 v0.20.1 中的 sendTransaction() ;send 必须指定发起的账户地址,而 call 可以直接调用。注意在 v1.0.0 中,contract 后面要加上.methods 然后才能跟合约函数名,这与 v0.20.1 不同;类似,v1.0.0 中事件的监听也要 contract 后面加.events。

运行测试脚本

有了测试代码,就可以运行并观察结果。mocha 默认会执行 test 目录下的所有脚本,但我们也可以传入脚本路径,指定执行目录。如果你环境中全局安装了 mocha,可以使用如下命令运行测试:

mocha tests

如果没有全局安装 mocha,就使用如下命令运行测试:

./node_modules/.bin/mocha tests

如果一切正常,我们可以看到这样的输出结果:

完整的工作流

到目前为止,我们已经熟悉了智能合约的开发、编译、部署、测试,而在实际工作中,把这些过程串起来才能算作是真正意义上的工作流。比如修改了合约代码需要重新运行测试,但是重新运行测试之前需要重新编译,而部署的过程也是类似的,每次部署的都要是最新的合约代码。

通过 npm script 机制,我们可以把智能合约的工作流串起来,让能自动化的尽可能自动化,在 package.json 中作如下修改:

"scripts": {"compile": "node scripts/compile.js","pretest": "npm run compile","test": "mocha tests/","predeploy": "npm run compile","deploy": "node scripts/deploy.js"
},

上面的改动中,我们为项目增加了 3 条命令:compile、test、deploy,其中 pretest、predeploy 是利用了 npm script 的生命周期机制,把我们的compile、test、deploy 串起来。

接下来我们可以使用 npm run test 运行测试,结果如下:

同理我们可以使用 npm run deploy 部署合约,结果如下:

快速学习-mocha 简介与入门相关推荐

  1. Snorkel 学习笔记 简介与入门 LF TF SF Labeling Functions Transformation Function Slicing Function 示例

    文章目录 Snorkel简介与入门 1. 背景 2. LF (Labeling Function) 标注函数 3. TF (Transformation Function) 转换函数 4. SF (S ...

  2. 小汤学编程之jQuery学习day01——简介、入门、选择器

    一.简介 1.下载     2.导入 二.入门 1.jQuery对象     2.jQuery对象与js对象相互转换     3.页面载入 三.选择器 1.基本选择器     2.层级选择器      ...

  3. 【软件开发底层知识修炼】十四 快速学习GDB调试一 入门使用

    前面几篇文章学习了链接器相关的内容.现在开始来学习GDB调试.我们的目的是通过这几篇文章将GDB调试完全学会. 文章目录 1 为什么需要GDB 2 GDB 的常规应用 3 GDB调试程序实例 4 总结 ...

  4. 【软件开发底层知识修炼】十五 快速学习GDB调试二 使用GDB进行断点调试

    上一篇文章我们学习了使用GDB的最基本方法:[软件开发底层知识修炼]十四 快速学习GDB调试一 入门使用 本篇文章将学习GDB的断点调试.断点调试是一种非常重要的调试方法. 文章目录 1 断点类型 2 ...

  5. OpenCV-Python实战(18)——深度学习简介与入门示例

    OpenCV-Python实战(18)--深度学习简介与入门示例 0. 前言 1. 计算机视觉中的深度学习简介 1.1 深度学习的特点 1.2 深度学习大爆发 2. 用于图像分类的深度学习简介 3. ...

  6. AI之FL:联邦学习(Federated Learning)的简介、入门、应用之详细攻略

    AI之FL:联邦学习(Federated Learning)的简介.入门.应用之详细攻略 导读       2019 年2 月,微众银行 AI 团队自主研发的全球首个工业级联邦学习框架 FATE(Fe ...

  7. TF学习——TF之Tensorboard:Tensorflow之Tensorboard可视化简介、入门、使用方法之详细攻略

    TF学习--TF之Tensorboard:Tensorflow之Tensorboard可视化简介.入门.使用方法之详细攻略 目录 Tensorboard简介 Tensorboard各个板块入门 Ten ...

  8. Ruby入门之零基础如何学ruby以及ruby的应用/快速学习ruby/学习ruby的流程是什么?...

    作者:李路 链接:https://www.zhihu.com/question/19552402/answer/22336708 来源:知乎 快速学习Ruby on Rails,这应该是个伪命题,没有 ...

  9. 快速学习编程语言,快速高效的入门

    大家真的不好意思,自己曾经的笔记没想到这么多人浏览,从2020年4月22号起我会每天自己写个博客争取吧所有课程编写完成,谢谢大家的支持,和关注. 最简单快速的理解的编程网站,https://studi ...

  10. 准备选择计算机方向,该怎样快速学习电脑知识?零基础到精通入门!

    怎样快速学电脑知识?零基础到精通入门学习,电脑是辅助人们工作的工具,想要熟练使用电脑就需要掌握相应的操作方法,对应零基础对电脑一窍不通的人,我们该怎么快速学习电脑操作呢? 怎样快速学电脑知识? 一.确 ...

最新文章

  1. java 多线程 事件_java 多线程-线程不安全案例
  2. 一文详尽系列之K-means算法
  3. 发光二极管pcb封装图画法_五个方面剖析SIP封装工艺,看懂SIP封装真正用途
  4. 【html 及 HTML5所有标签汇总】★★★
  5. css有些效果不显示,css导入成功但没有效果怎么办
  6. 修改引入表打造穿透KIS6的下载者(转)
  7. 编程面试题之——简答题(持续更新...)
  8. python定时器 是线程吗_定时器中断线程python
  9. 获取pycharm 2016.1.4 注册码 (window系统) 2016.3注册码(Ubuntu系统)
  10. linux进程优雅退出,Golang信号处理及如何实现进程的优雅退出详解
  11. 抖音快手火山 热门采集/个人主页无水印视频批量解析下载工具2019-11-11
  12. 2020华为春招面试:一面二面三面(一下午4h左右完成)已挂
  13. 锐龙r55600G和r55600x区别 r5 5600x和r5 5600g性能差别大吗
  14. 静态生存期和动态生存期
  15. CVE-2020-2905: VirtualBox 虚拟机逃逸漏洞通告
  16. HTTP Status 500 - Failed to evaluate expression 'ROLE_USER'
  17. 云安全 | 云访问安全代理 CASB
  18. FPGA并行编程-以HLS实现数字信号处理
  19. [题解]bzoj4826 HNOI2017 影魔
  20. 23种设计模式(三) —— 手写实现 Template Method 模式 (将具体交给子类)

热门文章

  1. 【OpenStack】Nova中的rebuild和evacuate(HA)
  2. macos可以升级到指定版本吗_错过Mac OS升级,找不到旧OS版本怎么办?
  3. Kryo工具类 序列化和反序列化操作工具类KryoUtils
  4. VUCA时代:软件架构解决复杂性之道
  5. 高绩效团队-VUCA时代的五个管理策略《二》—代际管理
  6. “梧桐杯”中国移动大数据应用创新大赛 - 智慧金融赛道Baseline
  7. OpenCV物体颜色检测(Python)
  8. springboot 配置日志文件
  9. qq相册回收站复原显示服务器繁忙,qq回收站里恢复的照片在哪看 qq照片回收站还原照片路径地址...
  10. 【Auto.JS】Autojs官方提取文档使用说明函数 (1)