测试驱动开发_?使用jest进行测试驱动开发
前言
本文将使用jest进行测试驱动开发的示例,源码在github。重点说明在开发中引入单元测试后开发过程,以及测试先行的开发思路。
本文的重点是过程以及思维方法,框架以及用法不是重点。
本文使用的编程语言是javascript,思路对其他语言也是适用的。
本文主要以函数作为测试对象。
环境搭建
假设项目结构为
.
├── README.md
├── package.json
├── src
├── test
└── yarn.lock
- 安装依赖
yarn add --dev jest
- 打开package.json, 修改scripts字段
"scripts": {"test": "jest"
}
之后把测试文件放在test文件夹下,使用yarn test
即可查看测试结果
开发
现在要开发一个函数,根据传入的文件名判断是否为shell文件。
先做好约定:
- shell文件应该以
.sh
结尾 - shell文件不以
.
开头 - 函数为名
isShellFile
下面来看下开发步骤是怎么样的。
文件初始化
在src目录下新建 isShellFile.js
touch isShellFile.js
然后一行代码也不写,在test目录下新建 isShellFile.test.js
可以注意到,测试文件的名与源文件名类似,只是中间多了个 .test
touch isShellFile.test.js
第一个用例
打开测试文件 test/isShellFile.test.js
,编写第一个用例,也是最普通的一个: bash.sh
const isShellFile = require('../src/isShellFile')test('isShellFile', () => {// 调用函数,期望它返回值为 trueexpect(isShellFile('bash.sh')).toBeTruthy()
})
运行 yarn test
, 结果如下:
FAIL test/isShellFile.test.js✕ isShellFile (2ms)● isShellFileTypeError: isShellFile is not a function^^^3 | test('isShellFile', () => {4 | > 5 | expect(isShellFile('bash.sh')).toBeTruthy()| ^6 | })
失败是意料之中的,因为 src/isShellFile.js
一行代码也没写,所以测试代码中第5行 isShellFile
无法进行函数调用。
完善源文件src/isShellFile.js
module.exports = function(filename) {}
这样 isShellFile
就可以作为函数被调用了。
再运行 yarn test
FAIL test/isShellFile.test.js✕ isShellFile (7ms)● isShellFileexpect(received).toBeTruthy()^^^Received: undefined3 | test('isShellFile', () => {4 | > 5 | expect(isShellFile('bash.sh')).toBeTruthy()| ^6 | })
又报错了,但这次报错原因跟上次不同,说明有进步。
这次报错原因是,期望函数调用返回值为真 , 但实际没有返回真 。
这是当然的,因为在源文件中,根本没有写返回语句。
为了让测试通过,修改 src/isShellFile.js
module.exports = function(filename) {+ return true}
运行 yarn test
, 测试通过了!
PASS test/isShellFile.test.js✓ isShellFile (3ms)Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 1.548s
Ran all test suites.
把上述修改,提交到版本控制系统中。
git add package.json yarn.lock src test
git commit -m 'feat: init jest test case'
第二个用例
观察我们的测试用例,发现太简单了,只有正面的用例,没有反面的、异常的用例
test('isShellFile', () => {expect(isShellFile('bash.sh')).toBeTruthy()
})
在 test/isShellFile.test.js
添加一个反面的用例
test('isShellFile', () => {expect(isShellFile('bash.sh')).toBeTruthy()
+ expect(isShellFile('bash.txt')).toBeFalsy()})
运行 yarn test
(可以发现,在开发过程中需要反复执行上述命令,有个偷懒的办法,执行yarn test --watch
,即可监听文件变化,自动执行测试用例)
FAIL test/isShellFile.test.js✕ isShellFile (6ms)● isShellFileexpect(received).toBeFalsy()^^^Received: true4 | 5 | expect(isShellFile('bash.sh')).toBeTruthy()> 6 | expect(isShellFile('bash.txt')).toBeFalsy()| ^7 | })
报错了,期望返回假,但函数返回的是真。这是因为,源文件中, isShellFile
函数永远返回真!
完善 src/isShellFile.js
逻辑
module.exports = function(filename) {- return true;
+ return filename.indexOf('.sh') > -1};
测试通过了
PASS test/isShellFile.test.js✓ isShellFile (4ms)Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 1.568s
Ran all test suites.
把上述修改提交到版本控制系统
git commit -am 'fix: 函数永远返回真的bug'
第三个用例
我们再添加一个用例,这次考虑特殊情况: .sh
这种文件,不算是shell文件。
修改 test/isShellFile.test.js
expect(isShellFile("bash.sh")).toBeTruthy();expect(isShellFile("bash.txt")).toBeFalsy();
+ expect(isShellFile('.sh')).toBeFalsy()
测试不通过
FAIL test/isShellFile.test.js✕ isShellFile (8ms)● isShellFileexpect(received).toBeFalsy()^^^Received: true5 | expect(isShellFile("bash.sh")).toBeTruthy();6 | expect(isShellFile("bash.txt")).toBeFalsy();> 7 | expect(isShellFile('.sh')).toBeFalsy()| ^8 | });
说明逻辑待完善,修改 src/isShellFile.js
module.exports = function(filename) {- return filename.indexOf(".sh") > -1;
+ let index = filename.indexOf(".sh");
+ return index > -1 && index != 0;};
测试通过(为精简文章内容,后面不再展示测试通过的输出),提交代码。
git commit -am 'fix: .sh应该返回false'
第四个用例
按照第三个用例的逻辑, .bash.sh
也不应该是shell文件,那么函数是否能正确判断呢,测试便知。
修改 test/isShellFile.test.js
expect(isShellFile('.sh')).toBeFalsy()
+ expect(isShellFile('.bash.sh')).toBeFalsy()
测试不通过
FAIL test/isShellFile.test.js✕ isShellFile (3ms)● isShellFileexpect(received).toBeFalsy()^^^Received: true6 | expect(isShellFile("bash.txt")).toBeFalsy();7 | expect(isShellFile('.sh')).toBeFalsy()> 8 | expect(isShellFile('.bash.sh')).toBeFalsy()| ^9 | });
说明逻辑待完善,修改 src/isShellFile.js
module.exports = function(filename) {let index = filename.indexOf(".sh");
- return index > -1 && index != 0;
+ return !filename.startsWith('.') && index > -1;};
测试通过,提交代码。
git commit -am 'fix: .开头的文件不算sh文件'
第五个用例
再考虑一种情况,如果 .sh
出现在中间呢?如 bash.sh.txt
, 它不应该是shell文件,来看看函数是否能通过测试。
修改 test/isShellFile.test.js
expect(isShellFile('.bash.sh')).toBeFalsy()
+ expect(isShellFile('bash.sh.txt')).toBeFalsy()
测试不通过
FAIL test/isShellFile.test.js✕ isShellFile (5ms)● isShellFileexpect(received).toBeFalsy()^^^Received: true7 | expect(isShellFile('.sh')).toBeFalsy()8 | expect(isShellFile('.bash.sh')).toBeFalsy()> 9 | expect(isShellFile('bash.sh.txt')).toBeFalsy()| ^10 | });
说明逻辑待完善,修改 src/isShellFile.js
module.exports = function(filename) {- let index = filename.indexOf(".sh");
- return !filename.startsWith('.') && index > -1;
+ let index = filename.lastIndexOf(".");
+ return !filename.startsWith('.') && filename.substr(index) == '.sh';};
测试通过,提交代码。
git commit -am 'fix: .sh必须在结尾'
重构
我们来观察目前 src/isShellFile.js
的函数逻辑
module.exports = function(filename) {let index = filename.lastIndexOf(".");return !filename.startsWith('.') && filename.substr(index) == '.sh';
};
对于 .bashrc
这样的文件,并不是shell文件,因为它是以 .
开头的。
则通过 filename.startsWith('.')
判断即可,前面的函数调用 filename.lastIndexOf(".")
是多余的。也即,目前的函数判断逻辑不够简明。
下面是一种优化思路:
module.exports = function(filename) {return !filename.startsWith('.') && filename.substr(filename.lastIndexOf(".")) == '.sh';
};
测试通过,提交代码
git commit -am 'refactor: 优化逻辑'
注意,这个重构示例的重点是:
- 先完成功能,再重构
- 重构必须要有测试用例,且确保重构后全部测试用例通过
至于其他方面,见仁见智,并不是重点。
结论
本文通过代码实例,践行了测试先行的理念。
文中的代码实现不是重点,而是开发过程。
文中 文件初始化 及 第一个用例 的内容,尤其值得回味,它体现了两个思路:
- 总是在有一个失败的单元测试后才开始编码
- 用必要的最小代码让测试通过
总的来看,TDD总是处于一个循环中:
- 编写用例
- 测试失败
- 编写代码
- 测试成功
- 提交代码
- 重复以上
通过这样,功能的实现每次都是最小成本的,功能也是有步骤地、通过迭代完成的,而不是一步登天。
更关键的是,完善的测试用例,是开发者的“守护天使”,有了它们,以后在添加新功能时,修改/重构代码都有了可靠的保障,让开发者可以充满信心,code with confidence !
扩展
使用babel
要想使用import/export语法,需要安装babel相关依赖
- 安装依赖
yarn add --dev babel-jest @babel/core @babel/preset-env
- 在项目根路径新增配置文件
babel.config.js
module.exports = {presets: [['@babel/preset-env',{targets: {node: 'current',},},],],
};
- 重新启动测试
yarn test --watch
为什么使用jest
因为这是vue官方工具链的一部分, 同时也可以为后续的组件测试作准备。
更多请点击查看2019-06-04尤雨溪的vue技术分享
测试驱动开发_?使用jest进行测试驱动开发相关推荐
- 测试驱动开发_DevOps之浅谈测试驱动开发
"测试驱动开发(Test-Driven Development, TDD),以测试作为开发过程的中心,它要求在编写任何产品代码之前,先编写用于定义产品代码行为的测试,而编写的产品代码又要以使 ...
- jest java_✅使用jest进行测试驱动开发
前言 本文将使用jest进行测试驱动开发的示例,源码在github.重点说明在开发中引入单元测试后开发过程,以及测试先行的开发思路. 本文的重点是过程以及思维方法,框架以及用法不是重点. 本文使用的编 ...
- 测试驱动开发 测试前移_测试驱动开发:它是什么,什么不是。
测试驱动开发 测试前移 by Andrea Koutifaris 由Andrea Koutifaris Test driven development has become popular over ...
- 如何使用 Django 进行测试驱动开发,我来告诉你
所谓测试驱动开发(TDD),就是先编写测试用例,然后编写代码来满足测试用例,具体包含以下步骤: 编写测试用例. 编写代码满足测试用例中的需求. 运行测试用例. 如果通过,说明代码满足了测试用例所定义的 ...
- 在敏捷中应用测试驱动开发
在敏捷和DevOps领域,企业越来越关注持续集成和持续部署问题.他们更频繁地更新软件,给软件测试造成额外的时间压力.而测试驱动开发可以成为解决这个问题的一剂良方. \\ 什么是测试驱动开发? \\ 测 ...
- 简单的11步在Laravel中实现测试驱动开发
测试驱动开发(英语:Test-driven development,缩写为TDD)是一种软件开发过程中的应用方法,由极限编程中倡导,以其倡导先写测试程序,然后编码实现其功能得名. 下文是我在Mediu ...
- 被高估了的测试驱动开发?
测试驱动开发(TDD)始于上世纪 90 年代,时至今时今日,依然只有少数的开发者在践行着.本文作者从软件开发者的角度,又一次帮助我们定义了测试驱动开发,解答了众多开发着对 TDD 常见的谬误. 作者 ...
- C++ 测试驱动开发
看到一篇非常好的C++测试驱动开发文章,这里转载下. 测试驱动开发(TDD)背景及综述 测试驱动开发是 Kent 提出的一种新的软件开发流程,现在已广为人知,这种开发方法依赖于极短重复的开发周期,面对 ...
- 面向 C++ 的测试驱动开发
测试驱动开发(TDD)背景及综述 测试驱动开发是 Kent 提出的一种新的软件开发流程,现在已广为人知,这种开发方法依赖于极短重复的开发周期,面对开发需求,开发人员要先开发代码测试用例,这些代码实现的 ...
最新文章
- Linux 下Shell脚本删除过期文件
- findcontours函数_opencv轮廓findContoursamp;drawContours
- protobuf简单序列化反序列化示例
- 登录多实例MySQL失败,修改密码临时解决,原因不明
- ES6——举个例子理解Promise的原理和使用
- 用new关键字对一个String 变量赋值和用literal值直接赋值有什么不同(转)
- 《我和他的结婚录像和相册集》的快速传播
- 计算机注册表管理,如何打开计算机注册表编辑器
- 移动端测试必须具备的技能
- String.GetEnumerator 方法的C#例子
- perl 教程网站 记录
- php imagick 取得psd缩略图,PHP中使用Imagick操作PSD文件实例
- 编辑手机pad端调试方法
- yoga book android rom,【发帖王】YOGA BOOK Android版玩机技巧
- 微信公众号的自定义菜单的创建
- 前端实现GIF图片压缩上传
- 绘制线性回归和多元线性回归
- loadrunner使用sitescope监测监控mysql数据库
- 使用ADB工具卸载/停用Android系统应用(无需Root)
- 大数据24小时:链家研究院发布地产大数据产品Real Data,上海交大与依图共建AI联合实验室
热门文章
- java stringjoiner_java-为什么我们已经拥有StringBuilder时使用StringJoiner?
- mysql 5.7.18源码包下载_MYSQL数据库CentOS6.9+Mysql5.7.18源码安装详细教程
- cron表达式在线测试
- matlab第六章课后答案,matlab作业第6章
- linux ora 27125,ORA-27125 unable to create shared memory segment | 信春哥,系统稳,闭眼上线不回滚!...
- mui-scroll-wrapper mui-scroll 内容增多不出滚动条
- 六:SpringCloud-Config
- video标签 api
- 『设计模式』之小试牛刀
- 只有IE能上网,其他浏览器均不可以!