美文网首页
[解锁新姿势] 兄dei 我感觉你在写bug

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

作者: HyZhan | 来源:发表于2019-11-09 08:59 被阅读0次

    前言:

    继上篇 [解锁新姿势] 兄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 开发模式,主要看开发者意愿,不强求,但是单元测试 必须的!

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

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

    相关文章

      网友评论

          本文标题:[解锁新姿势] 兄dei 我感觉你在写bug

          本文链接:https://www.haomeiwen.com/subject/qopgbctx.html