崇明建设镇网站,三明住房和城乡建设部网站,wordpress媒体库图片,韶关企业网站建设公司01、前言
很早之前#xff0c;曾在网络上见到过 TDD 这 3 个大写的英文字母#xff0c;它是 Test Driven Development 这三个单词的缩写#xff0c;也就是“测试驱动开发”的意思——听起来很不错的一种理念。
其理念主要是确保两件事#xff1a;
确保所有的需求都能被照…01、前言
很早之前曾在网络上见到过 TDD 这 3 个大写的英文字母它是 Test Driven Development 这三个单词的缩写也就是“测试驱动开发”的意思——听起来很不错的一种理念。
其理念主要是确保两件事
确保所有的需求都能被照顾到。在代码不断增加和重构的过程中可以检查所有的功能是否正确。
但后来很长一段时间里都没再听过 TDD 的消息。有人说TDD 已经死了给出的意见如下
1通常来说开发人员不应该在没有失败的测试用例下编写代码——这似乎是合理的但是它可能导致过度测试。例如为了保证一行生产代码的正确性你不由得写了 4 行测试代码这意味着一旦这一行生产代码需要修改你也得修改那 4 行测试代码。
2为了遵循 TDD 而写的代码容易进入一个误区代码是为了满足测试用的而忽略了实际需求。
02、TDD 到底是什么
不管 TDD 到底死了没有先让我们来回顾一下 TDD 到底是什么。
TDD 的基本思想就是在开发功能代码之前先编写测试代码。也就是说在明确要开发某个功能后首先思考如何对这个功能进行测试并完成测试代码的编写然后编写相关的代码满足这些测试用例。然后循环进行添加其他功能直到完成全部功能的开发。
TDD 的基本过程可以拆解为以下 6 个步骤
1 分析需求把需求拆分为具体的任务。
2 从任务列表中取出一个任务并对其编写测试用例。
3 由于没有实际的功能代码测试代码不大可能会通过红。
4 编写对应的功能代码尽快让测试代码通过绿。
5 对代码进行重构并保证测试通过重构。
6 重复以上步骤。
可以用下图来表示上述过程。 03、TDD 的实践过程
通常情况下我们都习惯在需求分析完成之后尽快地投入功能代码的编写工作中之后再去调用和测试。
而 TDD 则不同它假设我们已经有了一个“测试用户”了它是功能代码的第一个使用者尽管功能代码还不太完善。
当我们站在“测试用户”的角度去写测试代码的时候我们要考虑的是这个“测试用户”该如何使用功能代码呢是通过一个类直接调用方法呢静态方法还是构建类的实例去调用方法呢实例方法这个方法如何传参呢方法如何命名呢方法有返回值吗
有了测试代码后我们开始编写功能代码并且要以最快地速度让测试由“红”变为“绿”可能此时的功能代码很不优雅不过没关系。
当测试通过以后我们就可以放心大胆的对功能代码进行“重构”了——优化原来比较丑陋、臃肿、性能偏差的代码。
接下来假设我们接到了一个开发需求 汪汪队要到小镇冒险岛进行表演门票为 99 元冒险岛上唯一的一个程序员王二需要开发一款可以计算门票收入的小程序。 按照 TDD 的流程王二需要先使用 Junit 编写一个简单的测试用例测试预期是销售一张门票的收入是 99 元。
public class TicketTest {private Ticket ticket;Beforepublic void setUp() throws Exception {ticket new Ticket();}Testpublic void test() {BigDecimal total new BigDecimal(99);assertEquals(total, ticket.sale(1));}}为了便于编译能够顺利通过王二需要一个简单的 Ticket 类
public class Ticket {public BigDecimal sale(int count) {return BigDecimal.ZERO;}}测试用例运行结果如下图所示红色表示测试没有通过预期结果是 99实际结果是 0。 那接下来王二需要快速让测试通过Ticket.sale() 方法修改后的结果如下
public class Ticket {public BigDecimal sale(int count) {if (count 1) {return new BigDecimal(99);}return BigDecimal.ZERO;}}再运行一下测试用例结果如下图所示绿色表示测试通过了预期结果是 99实际结果是 99。 绿了绿了测试通过了到了该重构功能代码的时候了。99 元是个魔法数字至少应该声明成常量对吧
public class Ticket {private final static int PRICE 99;public BigDecimal sale(int count) {if (count 1) {return new BigDecimal(PRICE);}return BigDecimal.ZERO;}}重构完后再运行一下测试用例确保测试通过的情况下再增加几个测试用例比如说门票销量为负数、零甚至一千的情况。
public class TicketTest {private Ticket ticket;Beforepublic void setUp() throws Exception {ticket new Ticket();}Testpublic void testOne() {BigDecimal total new BigDecimal(99);assertEquals(total, ticket.sale(1));}Test(expectedIllegalArgumentException.class)public void testNegative() {ticket.sale(-1);}Testpublic void testZero() {assertEquals(BigDecimal.ZERO, ticket.sale(0));}Testpublic void test1000() {assertEquals(new BigDecimal(99000), ticket.sale(1000));}}销量为负数的时候王二希望功能代码能够抛出异常销量为零的时候功能代码的计算结果应该为零销量为一千的时候计算结果应该为 99000。 有两个测试用例没有通过那么王二需要继续修改功能代码调整如下
public class Ticket {private final static int PRICE 99;public BigDecimal sale(int count) {if (count 0) {throw new IllegalArgumentException(销量不能为负数);}if (count 0) {return BigDecimal.ZERO;}if (count 1) {return new BigDecimal(PRICE);}return new BigDecimal(PRICE * count);}}再运行一下测试用例发现都通过了。又到了重构的时候了销量为零、或者大于等于一的时候代码可以合并于是重构结果如下
public class Ticket {private final static int PRICE 99;public BigDecimal sale(int count) {if (count 0) {throw new IllegalArgumentException(销量不能为负数);}return new BigDecimal(PRICE * count);}}重构结束后再运行测试用例确保重构后的代码依然可用。
04、最后
从上面的实践过程可以得出如下结论 TDD 想要做的就是让我们对自己的代码充满信心因为我们可以通过测试代码来判断这段代码是否正确无误。 也就是说TDD 流程比较关键的一环在于如何写出有效的测试代码这里有 4 个原则可以参考
1测试过程应该尽量模拟正常使用的过程。
2应该尽量做到分支覆盖。
3测试数据应该尽量包括真实数据以及边界数据。
4测试语句和测试数据应该尽量简单容易理解。
注意这 4 个原则不仅适用于 TDD同样适用于任何流程下的单元测试。
最后我想说的是不管 TDD 有没有死TDD 都不是银弹不可能适合所有的场景但这不应该成为我们拒绝它的理由。 【B站最全最易学】十年大佬终于将测试开发路线整理出来了小白一学就会拿走不谢允许白嫖