作为一名开发人员,我们经常会听到这样一句忠告:"在开发软件时编写写测试代码(单元测试,功能测试等)能有效的减少产品中的bug",如何来验证这种言论呢,最大的验证案例应该就是TDD(测试驱动开发),下面本文将通过TDD这种开发技术来改进你的代码质量和稳定性

(一、)什么是TDD

测试驱动开发(Test Driven Development)简称TDD,最初概念始于1993年,于2003年兴起,逐渐被大众接受。

TDD是一种软件开发理念,与极限编程概念类似“测试优先”,可以通过3个步骤来学习到TDD的开发法则.

  1. 将需求转换具体的测试用例
  2. 对软件需求进行编码&&编写通过测试用例
  3. 对软件代码进行重构,消除冗余代码

每次有需求变更或者功能增加,可重复循环这3个步骤来完善你的代码,使它最大化减少错误率,也是一个完整的TDD开发周期

(二、)TDD的好处

TDD的优点很多,一下列举了个人认为比较重要的一些

  1. 任务分解,开发人员更了解用户:通常开发人员在编写业务的时候,大多数只会考虑到Input与Output,并没有转换到用户的角度去思考产品(面向用户编程),通过TDD,开发人员会更加站在用户角度思考,会深度考虑用户可能进行的所有操作,而不是站在开发者角度想用户应该会如何去使用我们的产f品.
  2. 测试用例覆盖率高,Bug减少:使用TDD的开发过程中,因为需要先编写测试,然后才能开始业务需求编码,当所有测试通过后,开发人员才能提交代码,这样会使得软件代码测试覆盖非常高,测试覆盖率高同时也表明代码是经过充分测试的,这样才正式环境运行遇到BUG也会相对少很多。
  3. 更清晰整洁的代码:在TDD的开发过程中,我们在不断重构代码,消除不好的代码,这会让我们的代码变得更加整洁和高效,从而改善优化我们的软件驾考,让项目变得更加灵活易扩展。
  4. 更安全的重构:重构改变的是代码的内部结构,而不会改变外部接口功能,由于代码的测试覆盖率高,由于代码的测试覆盖率高,每个功能都有对应的测试代码,开发人员可以更大胆进行重构,因为有充分的测试代码,当我们重构时,如果破坏了原有的功能,测试就会马上失败。
  5. 更好的信心,因为存在大量测试用例,你不用担心每次提交的新代码都会影响到项目中之前的服务运行,每次运行测试用例即可检测到你的代码是否会影响到原来系统的运行,极大提高了开发者的自信。

(三、)实现第一个简单的TDD

下面将通过PHP代码来展示一个简单的小案例,将输入的金币兑换成余额,余额只保留整数,余额不可为负数

1.编写测试用例

按照预期结果去编写自动化参数的测试用例.

<?php

namespace Tests\Feature;

use App\Exchange;
use Tests\TestCase;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Foundation\Testing\RefreshDatabase;

class ExchangeTest extends TestCase
{
    public function exchangeBalance()
    {
        $gold = mt_rand(1,100000);  //随机输入金币
        $balance = Exchange::computeAmount($gold);  //计算兑换金额;
        //断言结果是否为整数余额
        $this->assertTrue($balance);
    }
}

此时我们立即运行该测试用例是失败的,因为业务需求还没实现,下面需要到我们的第二步.

2.编写业务代码实现

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Exchange extends Model
{

    const RATE = 600;

    public static function computeAmount($gold)
    {
        return intval($gold / self::RATE);
    }

}

3.运行测试

测试1:
    Command: run ExchangeTest
    自动化参数:3000
    运行返回结果:5
    
测试2:
    Command: run ExchangeTest
    自动化参数:-3000
    运行返回结果:-5

运行ExchangeTest测试用例,发现这次虽然返回了兑换后的余额,第一次运行成功,第二次运行失败,因为不符合第二项不可出现为负数的结果.

4.改进实现

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Exchange extends Model
{

    const RATE = 600;

    public static function computeAmount($gold)
    {
        if($gold <= 0){
            return 0;
        }

        return $gold / self::RATE;
    }

}

5再次运行测试

测试:
    Command: run ExchangeTest
    自动化参数:-3000
    运行返回结果:0

运行成功,符合我们的业务需求,提交代码。

4.TDD学习总结

当年学习了TDD之后,你就掌握到了任务分解、小步快跑的这种开发方式,你可以把它应用到你没有太大自信的功能开发中,他将提高你的代码质量和整洁度,来减少生产Bug的产生率,同时TDD的关键部分在于驱动(driven),要让测试驱动我们来进行功能开发,每写一个测试,都驱动我们写更多的生产代码,都在向实现我们的功能的方向前进。

最后想要良好的使用TDD或者应用到工作模式中,是需要不断练习的,掌握TDD的秘诀,就是要让你的代码变得具有可测试性,来让它更好的覆盖到你的项目中。

关于TDD的争议性依然存在很大的分歧,个人的观点是:具体的权衡还是需要落实到项目实际的实施上,在时间允许的过程中,每个重要的函数都可以编写单元测试,覆盖正确性测试、边界测试、异常测试等等。之后的代码改动再运行一遍测试即可,这样会节省到许多开发时间,也提高了项目的稳定性,最后也治好了部分开发者不爱写文档和测试用例的习惯。


Sinming
310 声望21 粉丝

Bug总工程师