前言

最近有幸跟随资深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. Scala: Case classes

    Case classes are like regular classes with a few key differences which we will go over. Case classes ...

  2. PAT甲级满分攻略|记一次考试经历

    一次考试经历 今天是"大雪",很冷. 来到隔壁的学校考试,记得上一次来河中医是两年前大一刚开学吧,那天晚上印象比较深刻,6个室友骑车到处闲逛.当时还不会Hello world. 很 ...

  3. python1:基础数据类型(上)

    https://www.geekdigging.com/2019/10/13/2870915864/ 1.数字 在python的数字有4钟数据类型,分别是: int(有符号整型) long(长整型) ...

  4. kafka - java.nio.file.FileSystemException

    在启动Kafka时报错无法启动 E:\kafka_2.12-2.3.1\kafka-logs\__consumer_offsets-48\00000000000000000000.timeindex. ...

  5. git 提交代码步骤

    拉取服务器代码,避免覆盖他人代码 git pull 查看当前项目中有哪些文件被修改过 git status 提交代码至缓存 git add . 将代码提交到本地仓库中 git commit -m “提 ...

  6. CF 1136A 1136B 1136C 1136D 1136E(Round546ABCDE)题解

    题目地址:https://codeforces.com/contest/1136 A: Nastya Is Reading a Book 题解:挨个判断即可,水题. 参考代码: #include< ...

  7. 2018HDU多校训练一 D Distinct Values

    hiaki has an array of nn positive integers. You are told some facts about the array: for every two e ...

  8. 利用Python爬虫轻松挣外快的几个方法(值得收藏)

    前言本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理.作者:小猿猿er 在下写了10年Python,期间写了各种奇葩爬虫,挣各种奇葩 ...

  9. Prometheus启动失败的问题

    1.yml文件格式错误 Prometheus是开箱即用的,但是我们用的时候因为新增监控项,所以我们需要修改配置文件.改了之后启动不了的话,第一件事就要想的是yaml文件的格式问题. yaml中允许表示 ...

  10. 《吊打面试官》系列-ConcurrentHashMap & HashTable

    你知道的越多,你不知道的越多 点赞再看,养成习惯 本文 GitHub https://github.com/JavaFamily 已收录,有一线大厂面试点思维导图,也整理了很多我的文档,欢迎Star和 ...