日照网络公司
扫描关注网站建设微信公众账号

扫一扫微信二维码

日照网站建设公司告诉你如何进行单元测试!

昊诺网络o2017-07-31 09:09:26o网络知识o

日照网站建设公司告诉你如何进行单元测试!


单元测试是将应用程序分解为尽可能小的函数,并创建可重复的、自动化的测试用例的过程。在同等条件下,这些测试用例应该一直产生相同的结果,它们是应用程序的灵魂,并为今后所有应用程序的代码提供构建的基础。如果没有单元测试,不常使用的函数可能长达数月都不会被发现有 bug;相反,通过使用单元测试,我们可以在任何代码合并到主干之前就验证每个系统函数的功能,不会等到代码实际应用到产品中时还会出现问题。

网站建设前端架构师的主要责任是确保开发者拥有尽可能效率高的开发工具。单元测试就是其中之一,它可以用于构建任何规模的应用,无论构建应用程序逻辑的主要语言属于前端语言还是后端语言,你都有大量的工具可以应用到工作流中。无论使用的是 PHP 的 PHPUnit、Node 的 NodeUnit,还是 JavaScript 的 QUnit,你都能找到用于构建单元测试的成熟稳定的平台。

虽然技术栈(和与之相关的测试)有可能留给软件架构师来决定,但前端开发人员写出的代码可能也需要测试。因此,熟悉尽可能多的测试工具很重要。掌握所有工具,甚至达到精通的程度,那是非常难的。但如果对基本概念有着深刻的理解,将能帮助你和你的团队编写出更加可测试的代码,并快速地掌握任何测试框架。

下面首先来回顾一下这些基本概念,然后再来看一些实际的代码。

1. 单元

“一次只做一件事,并把它做好”是构建基于单元测试的应用程序的原则。我们在写函数时经常想同时实现很多功能,结果不仅降低了效率,还增加了测试的难度,因为这样的函数无法复用。

思考一个简单的函数:通过客户的地址,计算出将产品从近的分拨区域运输给客户的运费。

下面来分解一下这个函数。函数要做的先是通过客户的地址找到近的分拨区域;然后使用分拨区域的地址,计算出分拨区域到客户地址的距离;使用这个距离,计算出将货物从点 A 运输到点 B 的费用。因此,虽然只有一个函数,它却做了 3 件相互独立的事:

(1) 根据地址找到近的分拨区域

(2) 计算两个地址之间的距离

(3) 根据距离计算运费

回顾一下“一次只做一件事,并把它做好”的理念,很明显可以通过 3 个独立的函数更好地实现通过地址来查询运费的功能。当我们对功能进行拆分后,就可以得到 3 个函数:一个根据地址找到近的分拨区域,一个计算两个地址之间的距离,还有一个根据距离计算运费。

(1)   更多重用

现在,这 3 个函数可以在整个应用中使用了,而不只是用来计算运费。如果程序的其他部分需要找到近的分拨区域或者计算两个地址之间的距离,可以直接调用这些已经写好的函数。现在,在较大的函数体中,我们不用一遍又一遍地重写功能相同的模块了。

(2) 更好的测试

我们可以测试每个独立且可重用的函数,而非测试应用程序所能计算的每一条运输路线。随着应用程序的发展,开发一个新功能所需要创建的新函数越来越少。这样我们用较少的低复杂度的函数完成了高复杂度的功能。

2. 测试驱动的开发

大多数人在一次接触单元测试时,可能写过一些功能代码以满足业务需求(比如上文中的计算运费的例子),然后也努力地将它重构为更小的、可重用的、可测试的代码,之后才去思考如何写测试用例。测试驱动的开发(test-driven development,TDD)则颠倒了这一思路,它将单元测试放在前一位,之后才是编写功能代码。

但如果为还没有创建的函数写测试用例,那些函数岂不是肯定无法通过测试吗?的确!但测试驱动的开发的目标是,通过测试用例来描述一个正确编写的系统应如何工作,并为实现这个系统来铺平道路。

对于上文提到的计算运费的例子,在测试驱动的开发中会为三个实现业务需求的函数分别编写单元测试。开发者的工作就是将一开始没有通过单元测试的函数变为能够通过测试的函数。先实现一个可以正确地查找出近的分拨区域的位置的函数,并通过前一个单元测试。然后,继续实现计算距离和计算运费的函数,得到三个通过与之对应的单元测试的函数。

完成上述步骤后,我们不仅实现了应用的计算运费的功能,还让这些函数有了完全的测试覆盖率。

3. 一个测试驱动的例子

单元测试的理念其实非常简单。它的基本思路是调用要测试的函数,传递一些预先设置好的参数,并描述结果应该是什么。下面来看看如何为通过给定距离来计算运费的函数设计功能测试:
素材图片
QUnit有多个用于测试断言的操作符,包括用于测试布尔值的 ok() 和用于比较复杂对象的 deepEqual()。在这个例子中,我们使用 equal() 来比较 calculateShipping() 的返回值与期望值。只要 calculateShipping(24) 返回的是 4(这个例子中确实将返回 4),这个单元测试就能通过。第三个参数——24 Miles——用来在测试输出中标记相应单元测试的结果。

上文中通过距离计算运费的单元测试(当然也包括其他函数的单元测试)就绪后,我们就掌握了一个测试集,通过运行该测试集可以确认系统能否正常工作。如果有人要改 calculateShipping() 的函数名或者运费,这个测试集就会反馈提示失败,因此我们能够在有问题的代码部署到生产环境之前就修复它们。

QUnit 的功能远远不止上文例子中提到的。举例来说,它可以测试同步函数,也可以测试异步函数;它还可以与单元测试中加载的 Web 页面进行交互(其实用 JavaScript 都能实现)。因此,如果你的单元测试包括用户点击鼠标或按下键盘时的返回值,QUnit 也能够支持。

4. 测试覆盖率要多大才足够

确定合适的测试覆盖率是很难权衡的一件事情。如果你不是在进行测试驱动的开发(这种开发中,没有代码是不需要单元测试的),那么确定测试覆盖率将非常重要。如果测试所有的代码,开发进度可能停滞不前;而如果测试不够,就有漏掉新问题的风险。

(1) 解决分歧点

如果为已有的项目设计单元测试,大部分情况下,你可能没有时间或者预算为现有的功能编写各个方面 覆盖率的测试集。但这不是问题!测试覆盖率的好处是,即使一个单一的测试也能够为系统建设贡献价值。因此,在决定从哪里开始写单元测试时,可以从能够获得大收益的地方开始。有时候,大的收益就是为系统简单的部分编写单元测试。就像在处理更重要的债务之前,先还清小额信用贷款一样,写一些简单但仍有价值的测试用例是为单元测试造势的好方法。

一旦有了能提供基本覆盖率的测试集,就可以开始寻找系统中关键的部分,或者过去频繁出问题的部分,在需求列表中为它们分别创建需求,并确保尽快推动这些需求。

(2) 从测试覆盖率开始

如果有幸作为网站建设前端架构师启动全新的项目,你的工作就不仅仅是设置好测试框架了,还要确保开发流程本身为单元测试做好了准备。就像写文档和进行代码审核一样,写单元测试也要花不少时间。你需要确保任何需要测试的需求都有额外的时间来编写单元测试,并且确认所需的测试覆盖率。

正如前面所说的,并不是所有的功能都需要同样的测试覆盖率。但前提是,每一个用户故事都是以测试覆盖率的相关任务作为开始的。只有当所有人都认为给这些任务写测试用例没有必要时,才考虑去掉它。这样我们才能确信,对于任何需要测试的功能,都已经安排了足够的时间去完成它们。

文章关键词