单元测试 & mocha 简述

单元测试 & mocha 简述

1. 单元测试

单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证

这个最小测试单元,可以是一个函数,可以是一个类,可以是一个对象,也可以是一个组件,一个插件

在软件开发周期中,单元测试一直占据着很重要的位置,因为单元测试属于白盒测试,也是测试流中最前的一步,所以它起到非常重要的作用

但是,单元测试在前端领域并不被重视,前端程序员也没有写单元测试的习惯

但是,随着前端技术的发展,随着node的发展,越来越多“非UI”的前端代码出现

单元测试是保证代码质量的重要环节之一,特别是这些代码是会提供给其他人使用的时候,比如node插件,grunt插件等等

单元测试的作用有许多,下面列举一些:

  1. 保证代码可用
  2. 另一种纬度的文档
  3. 方便迭代回归
  4. 驱动开发

2. mocha

mocha是一个优秀的js测试框架,在许多开源插件的测试模块都可以看到它的影子

它支持TDD/BDD等多种流行的接口,也接受多种Assertions,如should.js/expect/chai/better-assert等,通过这些即可构建各种风格的测试用例

2.1 BDD

BDD(Behavior Driven Development),行为驱动开发是一种敏捷软件开发的技术,是一种理论方法学,具体的读者可深入学习,这里不做深入

BDD的接口有:

  • describe():描述场景,在里面可以设定Context,可包括多个测试用例,也可以嵌套场景
  • it():位于场景内,描述测试用例
  • before():所有测试用例的统一前置动作
  • after():所有测试用例的统一后置动作
  • beforeEach():每个测试用例的前置动作
  • afterEach():每个测试用例的后置动作

如果知道测试驱动开发,也可以理解BDD

2.2 Assertions

断言,在程序里面是确保“某个判断”是对的,如果错了,那程序就会有问题

每种程序语言都有原生的assertion模块,node的是assert模块

assertion模块的核心原理就是利用异常,如果判断是对的,则相安无事,如果错了,就会抛出一个异常

原生的assertion模块在语意上可能不是很直白,因此,有许多开源的assertion库,比如should.js/expect/chai/better-assert等,让assertion变得更加语义化,甚至可以让非程序员也能看得懂,这对于测试驱动开发有很大的帮助

2.3 举个例子

说了那么多,下面举个例子: 现在我们写一个数组去重的函数,并对这个函数进行单元测试,如下:

var should = require('should');

function unique(arr) {
    var ret = [];
    var obj = {};

    if (arr && arr.length) {
        arr.forEach(function(v) {
            obj[v] = 1;
        });

        for (var k in obj) {
            if (obj.hasOwnProperty(k)) {
                ret.push(k);
            }
        }
    }

    return ret;
}

describe('unique', function() {
    it('should return empty array when the argument is null or empty array', function() {
        unique(null).should.be.eql([]);
        unique([]).should.be.eql([]);
    });

    it('should return a new array of uniqued items', function() {
        var testArr = [2, 1, 3, 1, 2, 2, 1, 4];
        var cloneArr = testArr.slice();
        var uniArr = unique(testArr);

        uniArr.should.have.length(4);
        uniArr.should.containDeep([1, 2, 3, 4]);
        testArr.should.be.eql(cloneArr);
    });
});

执行mocha test.js,结果如下:

我们这里只测试了两个用例:

  1. 当传入数组是null或者是空数组的时候,返回的是空数组
  2. 应该返回一个新数组,并且是去重

实际上,每个测试用例都应该是遵循SPR的,前面两个测试用例都应该分开一些的,但为了篇幅考虑就组合到一起了

但是,这些测试够吗?

如果我们更加严谨的话,我们要求传入参数必须是数组

如果我们的需求是去重之后的数组必须是stable的

我们需要加上这两个测试用例:

it('should return empty array when the argument is not an array', function() {
    unique({
        length: 1
    }).should.be.eql([]);
});

it('should return a stable array', function() {
    unique([2, 1, 3, 1, 2, 2, 1, 4]).should.be.eql([2, 1, 3, 4]);
});

运行测试会发现这两个用例失败了:

用例不过,于是,我们修改我们的unique函数:

function unique(arr) {
    var ret = [];
    var obj = {};

    if (arr && arr instanceof Array) {
        arr.forEach(function(v) {
            if (!obj[v]) {
                ret.push(v);
                obj[v] = 1;
            }
        });
    }

    return ret;
}

再次执行测试,我们会发现测试用例都通过了:

3 小结

从上面的例子我们可以看到,利用mocha框架来构建单元测试用例是很简单的,构建的测试用例也非常简洁

另外,通过例子我们可以看到,在编写测试用例的时候是可以和代码本身分离的,读者可以看下测试用例的代码(忽略掉unique函数的实现代码,实际上测试代码和被测试代码本来就是分开的),可以说,这部分代码已经非常语义化了

最后,通过上面例子的推演,我们可以发现,测试用例不仅与被测试代码分离,它应该和需求紧密结合,unique之后的数组是不是stable的,那完全是看需求的。

另外,当组件版本升级的时候,功能可能变多了,那这时候相应的测试用例也应该加上,一个优秀的测试框架是应该很好的支持轻易添加测试用例的,比如mocha那样

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏编程

一行 Python 代码实现并行

译者:caspar 译文:https://segmentfault.com/a/1190000000414339 原文:https://medium.com/b...

1879
来自专栏JackieZheng

AngularJS in Action读书笔记2——view和controller的那些事儿

今天我们来818《angularjs in action》的第三章controller和view。 1.Big Picture概览图 ? image.png...

18510
来自专栏Golang语言社区

【翻译】为什么 goroutine 的栈内存无穷大?

一些 Go 语言的新学习者总是会对 goroutine 栈内存占用大小感到非常好奇。这一般是由于程序员进行无限的函数循环调用导致的。为了说明这个问题,请思考以下...

3386
来自专栏Golang语言社区

Golang 语言gc 问题

在实际使用go语言的过程中,碰到了一些看似奇怪的内存占用现象,于是决定对go语言的垃圾回收模型进行一些研究。本文对研究的结果进行一下总结。 什么是垃圾回收? 曾...

35216
来自专栏mini188

学习笔记:内存,堆栈,到底为何物?

     在网上看到了一篇关于面试的博客文,突然发现自己对于这个博主而言简直差的是十万八千里,他提到的许多技术我尽然一个也答不上来。于是就开始反思,还是要抱一抱...

1716
来自专栏偏前端工程师的驿站

JS魔法堂:函数节流(throttle)与函数去抖(debounce)

一、前言                                     以下场景往往由于事件频繁被触发,因而频繁执行DOM操作、资源加载等重行为,导致...

1756
来自专栏文渊之博

理解和使用SQL Server中的并行

    许多有经验的数据库开发或者DBA都曾经头痛于并行查询计划,尤其在较老版本的数据库中(如sqlserver2000、oracle 7、mysql等)。但是...

1909
来自专栏木宛城主

Unity应用架构设计(5)——ViewModel之间如何共享数据

对于客户端应用程序而言,单页应用程序(Single Page Application)是最常见的表现形式。有经验的开发人员往往会把一个View分解多个SubV...

1836
来自专栏种道伟的专栏

Lua 性能剖析

lua语言在游戏行业大受欢迎,因运行效率高(相比于其他脚本语言),热更方便等原因被广泛应用。想在现有c++系统中引入lua,被挑战的第一个问题往往是:“Lua性...

3.2K3
来自专栏Golang语言社区

Golang语言学习-并发

goroutine: 由GO运行环境管理的轻量级线程 channel: 有类型的管道,操作符为 <- 数据流向箭头指向的方向 使用make(chan ...

3438

扫码关注云+社区