单元测试篇2-TDD三大法则解密
引言
在我们上一篇文章了解了单元测试的基本概念和用法之后,今天我们来聊一下 TDD(测试驱动开发)
测试驱动开发 (TDD)
测试驱动开发英文全称是Test Driven Development 简称 TDD。
根据 UncleBob 的 TDD 描述总结
我们先创建一个测试项目
直接在 VS 创建即可,可以参考上一篇文章的创建过程
The Three Laws of TDD.
- You are not allowed to write any production code unless it is to make a failing unit test pass.
- You are not allowed to write any more of a unit test than is sufficient to fail; and compilation failures are failures.
- You are not allowed to write any more production code than is sufficient to pass the one failing unit test.
这是本文的描述的三个 TDD 开发的原则,它确保了代码的质量和可维护性。
下面对这三条内容做详细的解释
- 第一条规则指出 不允许编写任何的生产代码,除非是在让单元测试通过时。
- 简单理解就是在编写任何实际的业务逻辑代码之前,必须先编写一个或者多个单元测试,这些单元测试因为没有实现所以会失败,有了失败的单元测试之后我们才可以去在生产代码中实现业务逻辑
第二条规则指出 不允许编写比失败所需更多的单元测试代码;编译失败也是失败:
- 这可以理解为 在编写单元测试时,应该只编写足够使测试失败的最小代码量。这样,可以立即知道新写的生产代码是否解决了问题。编译失败同样被视为测试失败,因为编译不通过意味着代码无法运行。
那一个我们上一章节的一个数学计算类的例子
namespace dotNetParadise_TDD.Test;
public class MathCalculatorTests
{
[Fact]
public void Add_TwoNumbers_ReturnSum()
{
// Arrange
var calculator = new MathCalculator();
// Act
var result = calculator.Add(3, 5);
// Assert
Assert.Equal(8, result);
}
}
因为我们没有 MathCalculator 这个类的实现所以,代码会编译失败。

在这个示例中,我们展示了如何编写一个简单的单元测试,测试 Calculator 类的 Add 方法是否能够正确地将两个数字相加并返回正确的结果。根据 TDD 原则,我们只编写了必要部分的代码来测试这个功能,并且在这个阶段测试应该会失败,因为 Add 方法还未实现。编译失败也会被视为测试失败,这强调了编写足够简洁和精确的单元测试的重要性,符合第二条准则。
- 第三条规则指出 不允许编写比通过单个失败单元测试所需更多的生产代码
可以理解为在编写生产代码时,只需编写足够让失败的单元测试通过的代码,而不是一次性编写完整的功能。这有助于保持代码的小步前进,并且每次更改都有明确的测试验证。
现在把MathCalculator类中增加一个参数*2 即翻倍的一个功能
第一步编写一个单元测试方法,
[Fact]
public void DoubleNumber_WhenGivenSingleNumber_ReturnsDouble()
{
// Arrange
var calculator = new MathCalculator();
// Act
var result = calculator.DoubleNumber(2);
// Assert
Assert.Equal(4, result);
}
第二步 编写足够让失败的单元测试通过的代码
namespace dotNetParadise_TDD.Test;
public class MathCalculator
{
public int DoubleNumber(int number)
{
throw new NotImplementedException();
}
}
接下来运行单元测试
dotNetParadise_TDD.Test.MathCalculatorTests.DoubleNumber_WhenGivenSingleNumber_ReturnsDouble
源: MathCalculatorTests.cs 行 20
持续时间: 371 毫秒
消息:
System.NotImplementedException : The method or operation is not implemented.
堆栈跟踪:
MathCalculator.DoubleNumber(Int32 number) 行 7
MathCalculatorTests.DoubleNumber_WhenGivenSingleNumber_ReturnsDouble() 行 26
RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)
结果和预期一样,测试没有成功

现在来重构一下这个方法
public int DoubleNumber(int number)
{
//throw new NotImplementedException();
return 2 * number;
}
再次运行单元测试

可以看到单元测试成功了!
TDD 开发流程图
最后
通常我们进行单元测试的时候都是先写业务逻辑,然后再单元测试,当系统业务逻辑变复杂之后可能会遗漏一些测试 Case。TDD 的出现就是解决这个问题,通过测试 Case 来写重构业务代码的模式。
这三个规则确保了 TDD 的核心循环:红(测试失败)、绿(测试通过)、重构。通过不断地重复这个过程,开发者能够编写出高质量、可测试、易于维护的代码。
单元测试篇2-TDD三大法则解密的更多相关文章
- 持续集成之单元测试篇——WWH(讲讲我们做单元测试的故事)
持续集成之单元测试篇--WWH(讲讲我们做单元测试的故事) 前言 临近上线的几天内非重大bug不敢进行发版修复,担心引起其它问题(摁下葫芦浮起瓢) 尽管我们如此小心,仍不能避免修改一些bug而引起更多 ...
- 看完这篇Redis缓存三大问题,保你面试能造火箭,工作能拧螺丝。
前言 日常的开发中,无不都是使用数据库来进行数据的存储,由于一般的系统任务中通常不会存在高并发的情况,所以这样看起来并没有什么问题. 一旦涉及大数据量的需求,如一些商品抢购的情景,或者主页访问量瞬间较 ...
- .net持续集成单元测试篇之单元测试简介以及在visual studio中配置Nunit使用环境
系列目录 单元测试及测试驱动开发简介 什么是单元测试 单元测试是一段自动化的代码,这段代码调用被测试的工作单元,之后对这个单元的单个最终结果的某些假设进行检验.单元测试几乎都是用单元测试框架编写的.单 ...
- Spring Boot 入门之单元测试篇(五)
博客地址:http://www.moonxy.com 一.前言 JUnit 是一个由 Java 语言编写的开源的回归测试(回归测试是指重复以前全部或部分的相同测试)框架,由Erich Gamma 和 ...
- .NET进阶篇04-Serialize序列化、加密解密
知识需要不断积累.总结和沉淀,思考和写作是成长的催化剂这篇很轻松,没有什么费脑子的,所以解析较少,代码较多,为数不多的拿来即用篇整个章节分布请移步 内容目录 一.概述二.序列化1.二进制文件2.XML ...
- 单元测试篇----cppUnit的安装与使用
在刚学习单元测试章节的时候,尝试着使用dev—c++来编译cppunit,但一直没成功,也尝试问过同学,一直没有很好的方法,因此浪费了不少时间.今天又耐心的尝式一下,意外成功了.以下是详细的安装步骤: ...
- Java学习第三篇:类的三大特征,抽象类,接口,final关键字
一.类的三大特征 1.封装性 (1).什么是封装 封装就是把抽象出的数据和对数据的操作封装在一起, 数据被保护在内部, 程序的其他部分只有通过被授权的操作(成员方法), 才能对数据进行操作. (2). ...
- PHP 基础篇 - PHP 中 DES 加解密详解
一.简介 DES 是对称性加密里面常见一种,全称为 Data Encryption Standard,即数据加密标准,是一种使用密钥加密的块算法.密钥长度是64位(bit),超过位数密钥被忽略.所谓对 ...
- TDD三大定律
You must write a failing unit test before you write production code. You must stop writing that unit ...
- 第三十篇 面向对象的三大特性之继承 supre()
继承 一 .什么是继承? 类的继承跟现实生活中的父.子.孙子.重孙子的继承关系一样,父类又称基类. Python中类的继承分为:单继承 和 多继承. # 定义父类 class ParentClass ...
随机推荐
- .NET周刊【2月第3期 2024-02-25】
国内文章 4.1k Star!全面的C#/.NET/.NET Core学习.工作.面试指南 https://www.cnblogs.com/Can-daydayup/p/18027117 DotNet ...
- 现代 CSS 解决方案:accent-color 强调色
accent-color 是从 Chrome 93 开始被得到支持的一个不算太新属性.之前一直没有好好介绍一下这个属性.直到最近在给一些系统整体切换主题色的时候,更深入的了解了一下这个属性. 简单而言 ...
- 02-Redis系列之-架构和高级API的使用
通用部分 通用命令 # 1-keys # 打印出所有key keys * # 打印出所有以n开头的key keys n* # 打印出所有以nam开头,第四个字母是a到z的范围 keys nam[a-z ...
- 【图论#02】岛屿系列题(数量、周长、最大面积),flood fill算法的代码实现与优化
岛屿数量 给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量. 岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成. 此外,你可以假设该网 ...
- ZYNQ 裸机模式下修改默认uart端口
## 背景 调试ZYNQ 裸机code, 调用 printf()后在UART端口无法看到打印信息输出,查看原理图后发现,板子用的UART 1作为默认串口调试接口,UART 0分配给了RS485使用,因 ...
- 服务网关ZUUL过滤器
过滤器两个功能: 1.其中路由功能负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础: 2.过滤器功能则负责对请求的处理过程进行预干预,是实现请求校验.服务聚合等功能的基础. 有4类 ...
- CT图像重建
20世纪70年代中期,在医学领域出现了一种神奇装置,名为"计算机辅助 X 射线断层成像仪"(简称CAT或CT),它能够在不损伤病人的情况下,提供人体从头到脚各部位的断层X射线图像. ...
- Vue3基础知识提炼
1.{{}} data2.""等同{{}},数据单向绑定v-bind: 简化 :3.v-if v-else4.v-for="(i, index) in array&quo ...
- 一文讲明白Java中线程与进程、并发与与并行、同步与异步
写在开头 ok,everybody,在过去的两周内,我们大体上讲完了Java的集合,在最后我们探讨了关于HashMap线程不安全的原因,又提出了ConcurrentHashMap这个线程安全的集合解决 ...
- Sharding-JDBC源码解析与vivo的定制开发
作者:vivo IT 平台团队 - Xiong Huanxin Sharding-JDBC是在JDBC层提供服务的数据库中间件,在分库分表场景具有广泛应用.本文对Sharding-JDBC的解析.路由 ...
