前言

最近有幸跟随资深ThoughtWorks咨询师熊节老师一起学习测试驱动设计,经过短暂的十几天培训,对测试驱动设计的基本原则、实践模式、技巧有了一点点初步的认识。

在此之前,经常自嘲我经历的公司实践也似乎是TDD, 这种实践往往都是由测试工程师来驱动开发者完成bug的修改,虽然也是测试来驱动开发,但是却与真正的TDD大相径庭。

什么是TDD

在维基百科中是这样对TDD下定义的:

测试驱动开发(英语:Test-driven development,缩写为TDD)是一种软件开发过程中的应用方法,由极限编程中倡导,以其倡导先写测试程序,然后编码实现其功能得名。测试驱动开发始于20世纪90年代。测试驱动开发的目的是取得快速反馈并使用“illustrate the main line”方法来构建程序。
测试驱动开发是戴两顶帽子思考的开发方式:先戴上实现功能的帽子,在测试的辅助下,快速实现其功能;再戴上重构的帽子,在测试的保护下,通过去除冗余的代码,提高代码质量。测试驱动着整个开发过程:首先,驱动代码的设计和功能的实现;其后,驱动代码的再设计和重构。

测试驱动开发也是国外许多优秀开发者向开发者们推荐的一种普遍适用的开发模式,而在熊节老师的培训课程中,他时刻在向开发者灌输来自TDD的三条原则,要求我们的编写生产代码前,一定应该先编写单元测试。

定律一:在编写不能通过的单元测试前,不可编写生产代码。
定律二:只可编写刚好无法通过的单元测试,不能编译也算不通过。
定律三:只可编写刚好足以通过当前失败测试的生产代码。
简单实践

在我之前的编码实践过程中,总是习惯梳理一遍逻辑后,在根据项目的实际情况对代码进行重构,而随着我自以为掌握了单元测试的技巧之后,就开始把逻辑代码往单元测试上套,导致这样的单元测试实际上并非为了实现测试,而仅仅只是程序的入口而已。

如果使用TDD的方法,则需要首先规划需要实现的目标,然后再定义测试方法和测试需要实现的逻辑。

例如,代码大概是这样的:

我的目标是实现对Schema对象的解析,测试类采用SchemaUnitTest,并采用“should_xxx_when_xxx”的命名方式,定义了测试方法“should_return_true_when_bool”,然后定义一个Schemas的类,再定义其需要实现的需求(断言),以及需求的实现。

对单元测试方法的命名,不同的书籍有不同的命名方法,在这个项目实践中,采用的是should命名方法,而在之前看过的《单元测试的艺术》一书中,使用的is_when_return_xxx的方式,这两者只是命名方法的不同,本质上没有任何区别;使用xunit和mstest实际上也没有太多区别。

此时,这个定义的方法GetParameter是未实现的,所以会进入一个“红-绿-重构”的工作流程。

1)编写一个会失败的测试,以证明产品中代码或功能的缺失。编写代码时,要假设产品代码已经能工作了,这样测试的失败就说明产品代码中有缺陷。例如我定义的GetParameter方法使用xunit进行测试会提示失败, 只有在添加需要的代码后,编译才能通过。

2)编写符合测试预期的产品代码,使测试通过,产品代码应该尽量简单。

3)重构代码。如果测试通过了,你就可以编写下一个单元测试,或者重构,消除异味或提高代码可读性。

最终,我完成了一个这样的方法。(即便是这样的代码,依然有许多可以进一步提升的空间。)

显然这是一个逻辑非常简单的代码,但是如果采用全键盘操作,不使用鼠标来完成,仍然耗费了我不少时间,这个过程中,也让我对Visual Studio的快捷键操作更加熟练。

测试的不同阶段

在我们的产品研发过程中,经常遇到以下三种不同形式的测试

  • 端到端测试:端到端测试侧重于软件功能应用层面的测试,主要使用人工或自动化的形式对用户界面进行测试。往往需要覆盖系统的各个功能,需要耗费的人力物力较大。
  • 服务测试:主要集中在服务接口层的测试,可以通过PostMan等测试工具对接口的稳定性和可用性进行测试。侧重于接口行为实现。
  • 单元测试:针对代码层面,例如单个方法或单个类实现的测试。属于白盒测试的一种。

三种测试从上到下实施的容易程度递增,但是测试效果递减。端到端测试最费时费力,但是通过测试后我们对系统最有信心。单元测试最容易实施,效率也最高,但是测试后不能保证整个系统没有问题。

在我们的项目实践中,更多的采用的依然是端到端测试的模式,似乎只有通过测试者的人肉测试,才能让我们的代码更加令人满意。

单元测试事实上极少在我们的项目中得到实践,其主要原因大概是因为要掌握单元测试方法,本身需要对开发者的主观能动性提出了更高的要求,但是996开发者...太容易内卷化了。

总结

写好单元测试从来就是技术活,有一段时间过分在意理论概念和工具的用法,忽略了实践,所以实际上看了好几本书,依然不知道如何写单元测试,这次参与了培训,终于摸到了一点点影子。

现阶段我大概可以这样做来逐步提高自己的技能水平:

  • 1、小步快跑,注意节奏:不要过度在意某个需求的快速实现,而是编写能够在五分钟内快速完成的代码,并确保其通过。代码行控制在五行以内,代码的缩进层次,控制在两到三层。
  • 2、练习,练习,再练习:写代码从来不是一件容易的事情,按照一万小时定律的说法,如果指望几天就熟练掌握显然不太现实,未来需要更加积极的练习,才能真正掌握。
  • 多思考、努力写好代码:写几行代码其实并不难,难的是写高质量的代码。不要急于代码实现,要多思考上下文逻辑,让代码更加优美。

参考资料:

TDD的简单实践的更多相关文章

  1. Thrift简单实践

    0.什么是RPC RPC(Remote Procedure Call - 远程过程调用),是通过网络从远程计算机上请求服务,而不需要了解底层网路技术的细节.简单点说,就是像调用本地服务(方法)一样调用 ...

  2. Java 异步处理简单实践

    Java 异步处理简单实践 http://www.cnblogs.com/fangfan/p/4047932.html 同步与异步 通常同步意味着一个任务的某个处理过程会对多个线程在用串行化处理,而异 ...

  3. Android 设计随便说说之简单实践(合理组合)

    上一篇(Android 设计随便说说之简单实践(模块划分))例举了应用商店设计来说明怎么做模块划分.模块划分主要依赖于第一是业务需求,具体是怎么样的业务.应用商店则包括两个业务,就是向用户展示appl ...

  4. c#中,委托Func的简单实践

    c# 委托Func的简单实践最近才真正的接触委托,所以针对Func类型的委托,做一个实践练习. 首先说一些我对委托的初级理解:"就是把方法当做参数,传进委托方法里". 我平时用到的 ...

  5. kafka原理和实践(二)spring-kafka简单实践

    系列目录 kafka原理和实践(一)原理:10分钟入门 kafka原理和实践(二)spring-kafka简单实践 kafka原理和实践(三)spring-kafka生产者源码 kafka原理和实践( ...

  6. SQL知识以及SQL语句简单实践

    综述 大家都知道SQL是结构化查询语言,是关系数据库的标准语言,是一个综合的,功能极强的同时又简洁易学的,它集级数据查询(Data Quest),数据操纵(Data Manipulation),数据定 ...

  7. ZooKeeper分布式锁简单实践

    ZooKeeper分布式锁简单实践 在分布式解决方案中,Zookeeper是一个分布式协调工具.当多个JVM客户端,同时在ZooKeeper上创建相同的一个临时节点,因为临时节点路径是保证唯一,只要谁 ...

  8. Spring 学习二-----AOP的原理与简单实践

    一.Spring  AOP的原理 AOP全名Aspect-Oriented Programming,中文直译为面向切面(方面)编程.何为切面,就比如说我们系统中的权限管理,日志,事务等我们都可以将其看 ...

  9. VueRouter爬坑第一篇-简单实践

    VueRouter系列的文章示例编写时,项目是使用vue-cli脚手架搭建. 项目搭建的步骤和项目目录专门写了一篇文章:点击这里进行传送 后续VueRouter系列的文章的示例编写均基于该项目环境. ...

随机推荐

  1. mac安装pip

    1.下载get-pip.py curl -O https://raw.github.com/pypa/pip/master/contrib/get-pip.py SaintKings-Mac-mini ...

  2. mysql那些事(1)手机号与座机号码如何存储

    创建mysql数据表的时候,经常会遇到手机号码和座机号码数据的存储问题. 先说手机号码:很多人喜欢使用数字来进行存储,手机号不涉及到运算,并且有时候要带括号,加号之类的字符,有时候还要以0开头.所以, ...

  3. 一句话总结flux,以及我们为何需要flux

    如果让你用一句话总结一下什么是flux,该怎么说? 官网上有这样的介绍:flux是一种思想,一种框架,是facebook给react... 这样的解释对程序员来说,显得过于抽象又不具体了. 阮老师的文 ...

  4. alloc 和 init都做了什么验证。

    结论: alloc负责分配内存和创建对象对应的isa指针: init只是返回alloc生成的对象.  所以alloc后,多次调用init,返回的对象是同一个! 代码如下: // // main.m / ...

  5. ggplot2|玩转Manhattan图-你有被要求这么画吗?

    本文首发于“生信补给站”,ggplot2|玩转Manhattan图-你有被要求这么画吗?更多关于R语言,ggplot2绘图,生信分析的内容,敬请关注小号. Manhattan图算是GWAS分析的标配图 ...

  6. 关于简单的Excel多页签底层导出_电子底账导出为例(.net core)

    [HttpPost] public ActionResult ExpEleAcc(string linknos) { string filenname = null; CommonResult< ...

  7. MySQL必知必会(数据分组,Group by和Having子句, Select子句的顺序)

    SELECT vend_id, COUNT(*) AS num_prods FROM products #GROUP BY子句可以包含任意数目的列,多行NULL值将分为一组 GROUP BY vend ...

  8. [TimLinux] JavaScript AJAX如何重定向页面

    1. AJAX 异步JavaScript + XML,用于不通过页面from表单,来发送数据到后端服务器中 2. 如何重定向 服务器后端无法直接将页面重定向,因为服务器后端传回的任何数据,都将被XML ...

  9. Linux下RocketMQ下载安装教程

    一.下载 1.官网下载:下载地址   2.百度网盘下载:下载地址 提取码:0g5a java开发工具下载地址及安装教程大全,点这里. 更多深度技术文章,在这里. 二.安装及启动 1.将zip文件上传到 ...

  10. BZOJ11208 宠物收养所

    最近,阿Q开了一间宠物收养所.收养所提供两种服务:收养被主人遗弃的宠物和让新的主人领养这些宠物.每个领养者都希望领养到自己满意的宠物,阿Q根据领养者的要求通过他自己发明的一个特殊的公式,得出该领养者希 ...