TDD笔记

1
2
3
4
5
6
7
8
graph TD
需求提出-->抽象出模型
抽象出模型-->转化出测试用例
转化出测试用例-->TDD:定义接口测试接口
抽象出模型-->|not TDD|code
TDD:定义接口测试接口-->提交PR并讨论
提交PR并讨论-->code
code -->整合

自己写测试的心得

普通写代码就是直接撸,写成啥是啥,错了就改
正确的写测试就是 先想好接口,根据接口来写测试用例,最后再写代码去通过测试
这就叫谋而后动

今天跟了一个测试教程
学会了测试语言describe it before after(mocha) expect(supettest)

在egg中写测试

这是应用的单元测试,不是插件和框架的单元测试

内置的egg-bin模块,这个模块已经内置了所有测试所需要的好用的模块。
包括:Mocha、co-mocha、power-assert,istanbul
测试mock辅助模块egg-mock

第一步,编写测试

基础讲解

第二步 配置测试

在==package.json==中配置==script.test==

1
2
3
4
5
{
"scripts":{
"test":"egg-bin test"
}
}

第三步 启动测试

1
npm test

打开TDD的正确姿势

TDD是什么

tested dirven development,测试驱动开发

TDD的核心

做好需求分析和设计,将==需求==拆解

为什么要测试,什么时候测试

因为错误不可避免,要排除错误减少损失
测试越早越好,因为错误绝大部分错误是在早期引入的,发现在中后期
错误造成的损失随着时间呈现指数级的增长,越早发现,成本越低。这是软件测试的作用

测试驱动开发的作用:测试是根据需求来的,测试用例就是各个需求点,只要通过测试,模块的行为就能确保正确。在修改的时候做测试也能确保没有修改出错误。

测试的概念

单元测试:用来对一个模块、一个函数或者一个类来进行正确性检验的测试工作。就是该部分能否正常工作

测试用例:进行单元测试使用的例子

单元测试不通过说明要么有bug,要么测试用例不正确

测试正确说明,对该函数的修改没有对原来的行为造成影响。

测试驱动,是测试的什么?
测试个模块的接口
检验是否满足规定的需求,找出与预期的差异

测试效果的指标

提高测试覆盖率,测试效率

测试的误区

盲目追求覆盖率

测试的工具

mocha

mocha是JavaScript的一种单元测试框架,既可以在浏览器环境下运行,也可以在Node.js环境下运行。

使用mocha,我们就只需要专注于编写单元测试本身,然后,让mocha去自动运行所有的测试,并给出测试结果。

mocha的特点主要有:

既可以测试简单的JavaScript函数,又可以测试异步代码,因为异步是JavaScript的特性之一;

可以自动运行所有测试,也可以只运行特定的测试;

可以支持before、after、beforeEach和afterEach来编写初始化代码。

describe

包裹it们

第一个参数是测试的名字

第二个参数是一个函数,函数里是it

it

是各个测试用例

第一个参数是测试用例的描述

第二个参数是一个函数,函数体就是测试的内容,assert们

supertest

网络测试
构造请求,expect断言 判断回复

node自带断言库 assert

如何写好测试

所有人都假设TDD以后软件质量一定提高到最高境界了,但是测试代码也是代码,也有合理不合理的问题,也有价值与维护成本,不好的测试代码会降低效率
那么如何写好测试代码呢

测试的核心是“可测试“,可测试意味着好的代码架构,写出可测试代码是优良测试的必要条件。

测试驱动开发不是测试人员驱动。
这种开发方法完全是开发人员自行实施的,要点在于开发功能之前先开发该功能的测试程序,当然一开始测试程序的结果是失败的,然后去实现功能,让测试结果成功通过
测试驱动也不是测试驱动项目运转

因为国内大多数程序员写的代码粒度太粗,无法测试。

编写测试的原则是,一次只测一种情况,且测试代码要非常简单。

TDD和时间的关系

如果时间够,自然可以TDD。如果时间不够,有人可以tdd,测试先行,不代表真的要先写测试。tdd像一种工具或者流程,其背后有它自己的思维,掌握了思维,没有按流程出招,或许就表示tdd已死了吧。如果楼主是指形式或者招式,那么其在某些情况下还是继续存活的。比如时间足够,而程序员足够热爱它

TDD只是一个概念,时间允许的话每个重要的方法业务都写单元测试,涉及正确性测试,边界测试,错误测试等等。之后代码有改动再跑单元测试就可以了。这样开发会节省挺多时间。

和时间的关系,写测试要消耗时间
这是一个问题,很多公司的迭代周期比较短,比如半个月。那么如何搞TDD呢

TDD的好处

TDD好处其实很简单:

  1. 帮你整理需求
  2. 帮你设计接口(空想的话很容易设计出屎)
  3. 帮你做regression和给以后重构做准备

提高程序健壮性

TDD会鼓励你对现有代码,更频繁,更敏捷地进行改变
它带给你的是,更少的regression,更少的debuggingtime,以及更多的勇气来refactor

是tdd重新让我编程的信心和乐趣。
我不爱用眼睛看检查运行结果,只要改动之后,一运行看到一长排的勾我就去冲杯香飘飘喝吧喝吧,喝了睡觉去了


过度测试是存在。但一些看似过度的testmethod确实揪出了bug。国内中小公司(没去过大公司,不知道啥情况)从上到下对测试总是抗拒的态度。他们会迷惑如何在需求变更中控制缺陷,却不愿试着作任何改变。整天鼓吹熬夜搬砖的人是没法沟通的:(。所以总是测试不够,而非过度

要会单元测试,没有deadline的威胁,就是时间要足够
适合思路发散常常迷糊的人,让人有安全感,能确定想法落实了
可以灵活一些,比较高层的feature在确保接口可测试的前提下,没必要完全tdd。

TDD是否已死先不说,很多程序员连写出基本的整洁代码都做不到,还能指望他先写测试吗?写代码绝对是个技术活,而不是体力活。所以,写代码需要不断修炼,比如code dojo, code kata 至于是否用TDD方式写代码,倒不一定。如果程序员的思维转变,写代码之前先考虑到测试用例、测试场景,也是一个很大的转变,即测试先行。

我觉得TDD不火的原因是它对developer的素质要求非常高。新手看到TDD会欢欣鼓舞,但是他们没有能力来实践。老手们在项目的压力下,早就麻木了,先写case还不如写好代码再补case呢,很多东西我还没时间想清楚,怎么写case?不如先写个小功能先,边写边改,等稳定下来再补case. 所以对老手来说也需要不断练习,才能熟悉TDD.

其实我们的目的是为了快速的交付有价值,有质量的产品或者服务,赢得公司的生存和发展空间。为了达到这个目的,我们有很多种的手段。但手段不是目的。
有很多盲目推崇TDD的所谓敏捷专家,其实是把手段当做目的,而忽略了问题的本质

首先理清一个价值观问题,是方法论为生产力服务还是生产力为方法论服务
TDD的错误在于,他没有尊重程序员尊重开发实际,让非生产代码通知生产代码

TDD 力量和问题都源自 test first。要能 test first,写代码之前要想得更清楚;代码得要有良好的可测试性,而为了可测试性要做一些不直接产生效益的工作;两者都既能推动思考,也能限制思考。TDD 是正确的思想:程序员要保证代码的正确性,和不完美的实践:程序员写测试。

目前我们团队也在想办法推行TDD,但是不是标准的TDD,而仅仅是测试先行,并且不用mock这样做可以在实现业务代码之前对于自己的程序要实现什么有一个清晰的认识,而且写程序的过程也变成了“跑通我的单元测试”的过程,这样更加有目标和针对性,当你看到你写完的业务代码能跑通单元测试的时候,是否有种游戏过关的成就感呢?等到全员都逐渐适应这种测试先行的做法,再“得寸进尺”,多要求一点点。

测试先行,怎么和开发做交互呢?什么时间调用开发的方法?
测试先行是说在实现业务逻辑前先写完单元测试,此时的单元测试肯定不通,之后再逐渐针对测试完善业务代码,最后跑通测试的时候业务代码也就完成了。不是让测试人员先测的意思

TDD解决了:1)我要开始写代码了,我从哪开始。 搞个main,还是搞个test2)我不知道那个家伙的代码写的怎么样,以后会不会捅个篓子,要求他TDD好了。CodeReview的时候也轻松点。飞机上写几个小时代码,下来编译一遍过还没有bug的人。你跳出来说,他没有tdd,他LowB,那你是不是有毛病。总之一句话,各种方法论都是解决一个问题——人不行。

软件开发说到底还是抽象和逻辑的艺术,TDD只是一种辅助开发手段,并不会减少因为开发人员素质不过硬或者工期短赶工而在开发过程中产生的抽象和逻辑问题。从来没听说过历史上哪位编程高手是因为什么方法论而练成的,人家是数据结构、算法、抽象和逻辑能力真的强大。对了,除了TDD,那什么敏捷、XP、结对、DDD等等,所有抛开实际抽象和逻辑问题的方法论,都是垃圾。

更重要的是 基础的是 数据结构算法 抽象 逻辑能力

TDD在什么情况下最有效?

  1. 一个老系统,
  2. 老系统有还不错的test case
  3. 有很多相关系统依赖老系统
  4. 老系统要推翻重做

先阐明观点,TDD没死,只是存活着在一些技术及配合要求比较高的团队,并且是挑业务的。我在互联网行业摸打滚爬了蛮久,历经各种项目,包括PC、游戏、云存储、电商等项目,TDD也是我这几年年一直在推动的事情,大部分失败了,也有些收获,这里大致把所了解以及个人见解说下。拿我所熟悉的互联网行业来说吧,其他传统软件行业经历时间太早,离当前较远,就不乱说了。现在互联网大都流行敏捷开发,其模式大家可自行度娘,但是真正做到的貌似没有,最少我没见过或者听过,很难用定性的词语来形容(语文不好),用个相似度来说吧,当前BAT公司排序为BTA(还是个人见解),用我个人的说法就是作坊式开发,忽略很多流程,只留下骨干的评审、开发、用例评审、转测、回归发布的流程。这个模式特点就是快,从需求提出到发布上线,一般都是一两周的时间,现有三周才能开发完成的业务。当然这里也有提出异议,说我太片面了,不过我说的大部分的互联网公司业务,保守估计60%的比例吧。解释完了互联网公司的开发模式,接下来我们回到TDD,至于TDD的概念,我就不科普了,自行度娘。其中TDD有一个特点非常明确,基本上可以作为是否做到的标签。这就是测试架构先于开发完成,并可对开发完成大部分模块进行测试(个人的理解,标准定义自行百度)。为了达到这个目的,我们搬出几个要素出来:时间、人、效果、方式。时间:什么时候完成,项目时间大约多久可以做TDD,TDD占项目时间大约多少比例合适。人:谁来做,一个人的项目我这里不谈,只谈多人协作项目,开发做还是测试来做?效果:要达成什么效果,以什么为合格的TDD的指标,功能覆盖?代码覆盖率?方式:要怎么做,开发一套自动化框架,还是用先协商接口用单测来完成,这里特别要提出的是,需要考虑后期维护的代价。

TDD 推行的最大问题在于,大多数程序员还不会「写测试用例」和「重构」。
我认为推行TDD主要在两个方面努力,即意识和能力(好像任何事情都是这两方面哈)。意识方面主要是:认识到自己原有编程方法的不足;搞清楚TDD的价值所在,如何弥补原有方法的不足;心态开放,勇于尝试新鲜事物,不要浅尝辄止,要持续改进。意识和能力没有先后关系,而是在不断学习和实践过程中同时提高。在这篇文章中,我更想谈的是能力。我见到初接触TDD的人常犯下面的错误:在声明测试方法后,便开始写实现代码;写完“所有”的测试代码才开始写实现;一次实现过多的代码(超出当前测试覆盖的业务);从不重构;测试实现细节而不是接口行为;TDD真是看起来容易,做起来难。

1
2
3
4
graph LR
Red-->Green
Green-->Refactor
Refactor-->Red

上面这个图一目了然,但其实每一步都是对能力有要求的:红测试先行并不是说不需要思考,直接开始写代码。在开始写代码之前要进行需求分析,将需求分解为任务列表,再从列表中挑选一个任务,转换成一组测试用例,然后不断循环去实现。测试代码其实是产品代码的“用户”,在写测试代码时你就要考虑如何“使用”产品代码,是一个实例方法还是一个类方法,是从构造函数传参还是从方法调用传参,方法的命名,返回值等。这时其实我们就是在做设计,而且设计以代码来体现,比在脑袋中空想要更直观。很多人不懂“意图式编程”,总是习惯先实现一个东西,再去调用它。而测试先行就要求先使用,再实现。这样能少走很多弯路,减少返工。无法把测试前置的原因往往在于,测试的是「实现」,也就是你写的是「白盒」测试。这样的测试根本没有价值,重构的时候会成为维护负担。而好的做法是测试「做什么」,而不是「怎么做」。绿以最快的速度让测试变绿,意味着我们通常用最直接但可能并不优雅的方式,比如复制代码。然后小步重构,直到符合简单设计的原则:通过所有测试每个概念都被清楚地表达没有重复没有多余的东西难的是要让实现刚好满足当前的测试,不做过度的设计,不写多余的代码。因为如果你写多了,除了引入复杂性以外,多的那部分就没有测试能覆盖到。或者你后面的测试写出来就能直接变绿,你就没办法按TDD的节奏进行下去了。重构首先要能识别坏味道,一些低级的Smell,很容易识别,比如:Magic Number,重复代码,太大的类,太长的方法,命名等。但更高级的如Feature Envy,Lazy Class等就比较难以识别。只要识别到Smell,知道用什么手法去重构,剩下的就比较简单了,现代的IDE,尤其是JetBrains的产品,对重构的支持非常强大,几乎都可以用快捷键完成。选对工具非常重要,善假于物能极大提高开发效率。但也不能过分依赖工具,要明白每一个手法背后的原理。所以推荐每个想要实践TDD的开发者,一定要先读《重构》。最后我想说:TDD不是银弹,不可能适合所有的场景,但这不应该成为我们拒绝它的理由。也不要轻易否定TDD,如果要否定,起码要在认真实践过之后。

TDD教程

总览

TDD的理解和讲解

基线描述

能够讲出来科学的TDD步骤,和TDD的三个主要的状态
掌握mock技巧,能够讲出几种mock的场景
了解SetUp的作用
会写TestCase
给出题目可以驱动着写出来
细节如下:

能够讲出来科学的TDD步骤,和TDD的三个主要的状态

正确的TDD的步骤至少包含下面的几个点,写一个TestCase,运行,得到期望的失败,写实现,重构

文章标题:TDD笔记

本文作者:Benny

发布时间:2020-06-08, 19:49:33

最后更新:2019-10-09, 18:43:06

原始链接:https://benny233.github.io/2020/06/08/TDD笔记/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

目录