单元测试篇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 ...
随机推荐
- 代码随想录算法训练营第二十七天| 39. 组合总和 40.组合总和II 131.分割回文串
39. 组合总和 卡哥建议:本题是 集合里元素可以用无数次,那么和组合问题的差别 其实仅在于 startIndex上的控制 题目链接/文章讲解:https://programmercarl.com ...
- mysql-编写脚本-批量插入数据
一.代码 -- 报工设置,添加数据 set @org_id = '租户id'; set @created_user = 'yike'; set @updated_user = 'yike'; set ...
- Docker下搭建MySql主从复制
在Docker环境下搭建MySql主从复制,阅读此文章默认读者具备基础的Docker命令操作. 一.环境 1.Docker版本:Docker version 24.0.5, build ced0996 ...
- 10 个解放双手的 IDEA插件,少些冤枉代码(第三弹)
大家好,我是小富- 好久没发这种实用贴了,最近用到了一些能提升工作效率的IDEA插件,给小伙伴们分享一下.相信我,我分享的这些插件,都是实实在在能解决实际开发场景中痛处的. 以往的两篇IDEA插件分享 ...
- Java中枚举配合switch语句用法-2022新项目
一.业务场景 项目开发中经常会遇到多条件判断的情况,如果判断条件少的话使用if/elseif/else还比较好处理,如果判断条件多的话,则在使用这种语句就不太合适. 如果是自定义的一些内容,比如不同的 ...
- liunx 前台打包的两个报错 Invalid value used in weak set - MIS国产化服务器不支持打包
错误1 Invalid value used in weak set Webpack4使用 mini-css-extract-plugin 最新版 压缩css 报 "Invalid valu ...
- VS Code Snippet Generator 插件 生成 vscode代码片段
VS Code Snippet Generator 插件 生成 vscode代码片段
- 音乐分层软件 spectralayers7 扒歌 简直就是黑科技
音乐分层软件 spectralayers7 扒歌 简直就是黑科技
- dist目录打war包命令 jar -cvf yourName_web.war *
进入dist目录 "build:war": "cd dist && jar -cvf ../yourName_web.war *",
- linux kernel 中进程间描述符的传递方法及原理
PS:要转载请注明出处,本人版权所有. PS: 这个只是基于<我自己>的理解, 如果和你的原则及想法相冲突,请谅解,勿喷. 前置说明 本文作为本人csdn blog的主站的备份.(Bl ...
