前言

最近有幸跟随资深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. 2019-2020-11 20199304 《Linux内核原理与分析》 第十二周作业

    ShellShock攻击实验 一.实验简介 2014年9月24日,Bash中发现了一个严重漏洞shellshock,该漏洞可用于许多系统,并且既可以远程也可以在本地触发 二.预备知识 1.shells ...

  2. docker镜像、docker容器导入导出命令

    一.docker镜像导入导出命令 导出命令: docker save -o <保存路径> <镜像名称:标签> docker save -o ./test.tar test:la ...

  3. 机器学习笔记(六) ---- 支持向量机(SVM)

    支持向量机(SVM)可以说是一个完全由数学理论和公式进行应用的一种机器学习算法,在小批量数据分类上准确度高.性能好,在二分类问题上有广泛的应用. 同样是二分类算法,支持向量机和逻辑回归有很多相似性,都 ...

  4. HDU5973 Game of Geting Stone(威佐夫博弈)

    Two people face two piles of stones and make a game. They take turns to take stones. As game rules, ...

  5. CodeForces999A-Mishka and Contest

    A. Mishka and Contest time limit per test 1 second memory limit per test 256 megabytes input standar ...

  6. numpy的基本API(三)——索引

    numpy的基本索引API iwehdio的博客园:https://www.cnblogs.com/iwehdio/ 1.单个元素的索引 对于一维数组,索引方式与内置的List相同.正索引从0开始,负 ...

  7. 【Git】Windows 配置 SSH-Key

    查看本地公钥是否存在 执行以下语句来判断是否已经存在本地公钥 cat ~/.ssh/id_rsa.pub 如果出现如下截图,则本地公钥不存在,继续按步骤进行. 如果看到一长串以 ssh-rsa 或 s ...

  8. Asp.Net WebApi一个简单的Token验证

    1.前言: WebAPI主要开放数据给手机APP,Pad,其他需要得知数据的系统,或者软件应用.Web 用户的身份验证,及页面操作权限验证是B/S系统的基础功能.我上次写的<Asp.Net MV ...

  9. 基于 asm 实现比 spring BeanUtils 性能更好的属性拷贝框架

    Bean-Mapping 日常开发中经常需要将一个对象的属性,赋值到另一个对象中. 常见的工具有很多,但都多少不够简洁,要么不够强大. 我们经常使用的 Spring BeanUtils 性能较好,但是 ...

  10. 带你快速了解Java锁中的公平锁与非公平锁

    前言 Java语言中有许多原生线程安全的数据结构,比如ArrayBlockingQueue.CopyOnWriteArrayList.LinkedBlockingQueue,它们线程安全的实现方式并非 ...