[解锁新姿势] 兄dei 我感觉你在写bug

前言:

继上篇 [解锁新姿势] 兄dei,你代码需要优化了 介绍一些代码的优化的小技巧

但是我们除了在代码编写上需要优雅, 还需要编写对应的测试用例, 以此来保证代码的质量。

在这篇我们继续在学习如何编写有保证质量的代码。

背景

在刚刚学习编程的时候,由于没有接触过单元测试/TDD 相关知识, 只是知道有这么回事,不以为然。导致工作的时候,拿到一个新需求,只知道埋头苦写。会出现以下场景:

产品:增加一个新功能 blalala.

我:好的没问题,一个星期撸出来

一个月后.......

我:我的接口写好了!可以测试了。

测试:你自己测过没 ??

我:放心 postman 测过了,稳的一批。(/滑稽)

一分钟后.......

测试:你的接口又双叕 报 500 了 (黑人问号??)

我:不可能!肯定是你的问题

一次偶然的机会,接触到 TDD 开发模式,从此打开新的世界。原来编程还可以这么玩 : )

TDD

TDD是测试驱动开发(Test-Driven Development)的英文简称, 是敏捷开发中的一项核心实践和技术,也是一种设计方法论。TDD的原理是在开发功能代码之前,先编写单元测试用例代码,测试代码确定需要编写什么产品代码。

说白了,就是开发功能代码之前,先编写测试用例代码`

我们先来看一个“经典”的 TDD 问题,入手学习 TDD 开发,感受 TDD 开发的魅力所在

需求:

编写一个程序,打印出从1到100的数字,将其中3的倍数替换成“Fizz”,5的倍数替换成“Buzz”。既能被3整除、又能被5整除的数则替换成“FizzBuzz”。如下图所示

image

分析

在动手写代码之前,我们要首先弄清需求的范围优先级,例如给出的FizzBuzz 需求,1-100就是范围,对应优先级是1,确定了需求的范围和优先级后,然后我们就可以开始coding~

编码

Step1

我们首先新建一个FizzBuzz测试类


public class FizzBuzzTest {

    @Test

    void show_raw_number(){

        FizzBuzz fizzbuzz = new FizzBuzz(1);

        assertThat(fizzbuzz.getResult()).isEqualTo("1");

    }

}

public class FizzBuzz {

    public FizzBuzz(int input) {

    }

    public String getResult() {

        return "";

    }

}

温馨提示:

测试类推荐使用 驼峰式命名,测试方法 推荐使用下划线命名

我们先创建一个 FizzBuzz 类, 创建 getResult()方法,空实现, 传入参数 1,断言(预期)fizzbuzz 输出结果是 1,先运行测试用例,会出现如下效果

image

小伙伴:你会不会写啊,这么简单的还用测试?还报错!!

别着急,测试驱动开发,讲究是的循序渐进的节奏

听我的,往下看 相信你会 getfeel :)

看到红灯 程序提示,我们预期结果(Expected)是 “1”, 但实际结果(Actual)是0,说明我们的程序有错。

接下来我们进一步来修改我们的测试用例,以此来通过测试用例。

修改如下:


public String getResult() {

    return "1";

}

然后再次运行我们的测试用例

image

温馨提示:

编写刚刚好通过测试用例的代码。

这次“完美”通过测试!然后我们开始编写下一个测试用例~

完美?? 黑人问号?? 拿刀哪个,先把刀放下 你听我说。

Step2

我们来测试 第一种情况,将其中3的倍数替换成“Fizz”,编写 3 的测试用例


@Test

public void show_fizz(){

    FizzBuzz fizzBuzz = new FizzBuzz(3);

    assertThat(fizzBuzz.getResult()).isEqualTo("Fizz");

}

创建一个show_fizz 方法, 这次输入参数为 3,然后继续运行测试用例

image

看到我们熟悉的 “红灯报错”,然后我可以继续修改我们的 getResult 方法 以此来通过测试用例。


public String getResult() {

    if (input % 3 == 0){

        return "Fizz";

    }

    return String.valueOf(input);

}

然后再次运行测试用例~

image

绿色!!通过测试!

看到这里,我们可以发现一个规律

红灯行,绿灯停

当测试用例是“红灯” 时,我们就应该动手编写出,能通过测试的测试用例。

当测试用例是 “绿灯”时,我们就应该停下来思考,下一个测试用例改如何编写。

Step3

我们继续小步前进,继续编写第二种情况,5的倍数替换成“Buzz”, 编写 入参 5 的测试用例


@Test

public void show_buzz(){

    FizzBuzz fizzBuzz = new FizzBuzz(5);

    assertThat(fizzBuzz.getResult()).isEqualTo("Buzz");

}

不出意外的话,是 红灯

image

我们继续修改getResult()方法,以此来通过测试用例。


public String getResult() {

    if (input % 3 == 0){

        return "Fizz";

    }

    // 新增

    if (input % 5 == 0){

        return "Buzz";

    }

    return String.valueOf(input);

}

然后继续运行测试用例,绿灯通过~

image

Step4

我们继续小步前进,考虑第三种情况,既能被3整除、又能被5整除的数则替换成“FizzBuzz”,编写 入参 15 的测试用例


@Test

public void show_fizz_buzz(){

    FizzBuzz fizzBuzz = new FizzBuzz(15);

    assertThat(fizzBuzz.getResult()).isEqualTo("FizzBuzz");

}

这里相信大家能猜到结果,红灯,这里我就演示结果了。然后我们修改对应 getResult 方法,通过测试用例


public String getResult() {

    if (input % 15 == 0){

        return "FizzBuzz";

    }

    if (input % 3 == 0){

        return "Fizz";

    }

    if (input % 5 == 0){

        return "Buzz";

    }

    return String.valueOf(input);

}

image

重构

Step1

我们编写完测试用例,但是代码出现了代码的坏味道——重复代码,有了测试用例,我们就可以很轻松的重构代码,接下来开始重构我们的代码。


public String getResult() {

    if (isDivisibleBy(15)){

        return "FizzBuzz";

    }

    if (isDivisibleBy(3)){

        return "Fizz";

    }

    if (isDivisibleBy(5)){

        return "Buzz";

    }

    return String.valueOf(input);

}

private boolean isDivisibleBy(int i) {

    return input % i == 0;

}

抽取一个 isDivisibleBy 方法,然后运行测试,看看修改后是否引入bug

image

测试通过,没问题,但在这里我们需要注意一点

每一次修改,都需运行一遍测试,避免修改引入bug,确保代码的正确性

Step2

测试通过后,我们继续进一步优化。


public String getResult() {

    String result = "";

    if (isDivisibleBy(3)) {

        result += "Fizz";

    }

    if (isDivisibleBy(5)) {

        result += "Buzz";

    }

    return result;

}

继续修改 getResult 方法,提取一个变量 result 作为返回值,然后运行测试。

image

出乎意料,这次修改竟然出现bug,正如刚刚所说,每次修改后,都需要运行测试用例保证代码的正确性。我们再来检查一下代码,并作出如下修改:


public String getResult() {

    String result = "";

    if (isDivisibleBy(3)) {

        result += "Fizz";

    }



    if (isDivisibleBy(5)) {

        result += "Buzz";

    }



    if (result.isEmpty()){

        result += input + "";

    }

    return result;

}

通过测试用例,发现当输入为1的时候,没有进行处理,添加相应判断。再次运行测试用例

image

测试通过,大功告成!

最后

最后剩下遍历 1~100 情况,相信难不倒你,感兴趣的朋友,可以自行编写~

至此,我尽量演示一个相对完整的TDD开发流程,麻雀虽小,五脏俱全。希望你能感受到 getfeel ~

我们再来总结一下:

  • 1、创建测试类

    • 名推荐使用 驼峰式命名,测试方法 推荐使用下划线命名
  • 2、分析需求范围优先级

  • 3、根据优先级编写写测试用例

  • 4、再编写业务代码

  • 5、然后编写测试用例刚好通过的代码

    • 红灯行,绿灯停

      • 当测试用例是“红灯” 时,我们就应该动手编写出,能通过测试的测试用例。

      • 当测试用例是 “绿灯”时,我们就应该停下来思考,下一个测试用例改如何编写。

  • 5、根据测试用例,重构,优化代码。

    • 每一次修改,都需运行一遍测试,避免修改引入bug,确保代码的正确性
  • 6、最终完成功能

第5点,不分先后,可以交替执行。

最后的最后

有些人觉得为什么要弄那么复杂,还要浪费时间写单元测试,为何不一把梭呢?

没有测试用例覆盖的代码,你敢保证代码的质量吗?

没有测试用例覆盖的代码,你敢进行代码重构吗? 你品你细品 (滑稽)

其实TDD 开发模式,主要看开发者意愿,不强求,但是单元测试 必须的!

以上就是全部内容,希望能帮助到你~

如有不妥,欢迎指出,大家一起交流学习。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,544评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,430评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,764评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,193评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,216评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,182评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,063评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,917评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,329评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,543评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,722评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,425评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,019评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,671评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,825评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,729评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,614评论 2 353

推荐阅读更多精彩内容

  • 做TDD是为什么? 关于TDD的概念、工具、技巧等,经典的书籍材料可能介绍的更为全面细致。这篇文章想分享的是从一个...
    武可阅读 2,599评论 2 21
  • 本文结构: 什么是 TDD 为什么要 TDD 怎么 TDD FAQ 学习路径 延伸阅读 什么是 TDD TDD 有...
    李浪溪_WaterLee阅读 75,161评论 16 168
  • 码农的产品和服务大都是以软件形式存在的,我们存在的价值之一就是快速提供高质量的软件产品或服务。如何保障软件的高质量...
    abel_cao阅读 947评论 0 10
  • 今天下午在朋友圈看到一个朋友拿到了东京大学的交换生资格,看到她晒出了学校的入学许可证。记忆回到一年前…… 这个姑娘...
    项南安阅读 458评论 0 0
  • 今天下班后做了三件事。 第一件事是参加瑜伽班,太难,遂决定放弃。 瑜伽班的老师是位帅气的印度小哥,他会唱带有民族风...
    Aliciahappy1127阅读 268评论 0 3