单元测试篇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 ...
随机推荐
- ThreadLocal原理解析
ThreadLocal源码分析 /* * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. * ...
- 【Azure Developer】使用Key Vault的过程中遇见的AAD 认证错误
在使用应用程序访问Key Vault获取密钥信息时,现后遇见了多种认证错误.使用的代码为: String keyVaultUrl = "https://test-xxx.vault.azur ...
- python用matplotlib或boxplot作图的时候,中文标注无法正常显示,乱码为小方框的解决办法
第一种 import matplotlib.pyplot as plt plt.rc("font",family="SimHei",size="22& ...
- MVVM --- 实现多层级通知
引言 在实际开发场景中,当ViewModel内的一个属性是一个 ObservableCollection<T> 或者是一个多层级 class 的时候,有可能有的需求需要 Observabl ...
- EL1008E: Property or field 'timestamp' cannot be found on object of type 'java.util.HashMap' - maybe not public-请求springboot-后台报错
一.问题由来 自己在使用node.js写的前端项目访问springBoot访问的后端项目的时候,springboot项目中报这个错,抛出这个异常. 详细的错误信息如下: Servlet.service ...
- $help console 里面的入口帮助文档
$help console 里面的入口帮助文档 Object.defineProperty(window, '$help', { get: function() { // 这里面this是window ...
- Pandas导出美化技巧,让你的Excel更出众
pandas的DataFrame可以通过设置参数使得在jupyter notebook中显示的更加美观,但是,将DataFrame的数据导出excel时,却只能以默认最朴素的方式将数据写入excel. ...
- FastWiki v0.1.0发布!新增超多功能
FastWiki 发布 v0.1.0 https://github.com/239573049/fast-wiki/releases/tag/v0.1.0 更新日志 兼容OpenAI接口格式 删除Bl ...
- Android 优雅的Activity回调代码封装
原文地址: Android 优雅的Activity回调代码封装 - Stars-One的杂货小窝 之前提到Jetpack架构组件学习(3)--Activity Results API使用 - Star ...
- 【ATCOER、D - ±1 Operation 2】前缀和+排序二分
import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.Arrays; public cla ...
