• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    迪恩网络公众号

[转]测试驱动开发在PHP中的应用

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

转载自: http://blog.csdn.net/oxware/article/details/55875

1.   TDD

测试驱动开发(Test-Driven Development, TDD),是近年来兴起的一种软件开发方法。作为一项最佳实践,测试驱动开发在XP方法中占有重要地位。其对开发效率、代码健壮性的显著改进已被越来越多的实例项目所证实。

TDD的主要精神可以概括为“测试先行,快速反馈”。在有实际功能代码之前就开始编写测试代码,通过测试来反映设计,使得测试代码成为事实上的设计文档。一开始测试代码甚至不能通过编译,因为其要测试的功能代码还未编写。我们通过编写刚好够用的功能代码使测试尽快通过,并且没有多余的功能。然后再增加测试使其意料之中地失败,于是又产生编码的需要。一定功能完成之后对代码进行重构,然后加入新的功能。这样不断快速的“失败/通过/重构(red/green/refactor)”的循环中,以测试推动编码需求,步步为营,产生简洁易懂、可测试、健壮性强、刚好够用无多余功能的优良代码,形成测试驱动的开发模式。

2.   PHP+TDD

TDD本质是一种软件工程上的开发方法,应该与具体语言环境没有依赖。但其在不同语言中的具体运用状况是不尽相同的。从语言角度来看,测试驱动开发起源于面向对象的Smalltalk社区,成型于Java语言。以Java的自动化单元测试框架JUnit的推出为标志,TDD跳出了理论和实验,广泛在实践中运用起来。从JUnit衍生出的xUnit 家族成为TDD应用中不可缺少的工具,使TDD在许多语言中的应用成为可能。本文采用的PHPUnit[1]就是其中之一。

TDD从诞生之始就带着强烈的OO色彩。在Java/C++等完全OO语言中,为了单独隔离测试某一给定的模块(单元测试),使用了多态接口这样的OO技术来实现隔离。

Robert Koss博士与Jeff LangrCUJ上的《Test Driven Development in C/C++》一文中,讨论了以C这样的过程语言实践TDD的方法,使用宏来实现自动化测试,以连接期(link-time)多态来实现隔离。

对于施行TDD来说,PHP又有其自身的语言特性。首先,PHP支持类,可以说是部分OO的。也因为如此PHPUnit才有可能模仿JUnit的结构。在PHP5中对OO的支持已有了相当大的改进和扩充。但是,PHP并不是生来就设计为OO的,它在很大程度上照顾了C的色彩。而且在国内PHP项目的现状来看,OOD/OOP也并不是主导思想。在广泛运用的稳定版本PHP4.x中,OO方面支持简单继承,但因为弱类型特性,所以谈不上多态的概念。目前国内对TDD方法的关注大多基于Java环境,以PHP为例的讨论并不多见。因此有必要进行一些探讨。

3.   例子

我们以利用PHP标准函数explode()实现另一标准函数strtok()[2]作为例子,具体说明如何在PHP项目中应用TDD。为区别于标准函数,我们将待实现函数命名为strtk()。运行环境为Apache 1.3.23PHP 4.1.1PHPUnit 0.6.2

3.1任务分析 —— 需求、设计、测试

程序员通过向strtk()传递两个字符串参数$str$sep,以及随后连续调用strtk($sep),会依次得到$str中被$sep里任一字符分割的每个非空子串。事实上,TDD不提倡事先列出详细的设计条款或者形成官僚繁冗的设计文档,因为“测试反映设计”,测试即是编码的设计文档,不断增加的测试对应着不断变化的需求。需求变化中的任何时刻我们的代码都是可用的,因为它们都是保证测试通过的。这与XP方法“主动拥抱变化”的精神是一致的。

3.2第一轮 —— PHPUnit、伪实现模式

首先想到容易测试的是$str中不存在分隔符串$sep的情况,结果应是原串$str。我们开始编写测试用例中的第一个方法:

//test for strtk()

require_once "PHPUnit.php";//使用PHPUnit

require_once "strtk.php";//被测函数所在文件

 

//一个测试用例即为一个继承了PHPUnit_TestCase的子类

class test_strtk extends PHPUnit_TestCase

{   

     //constructor

     function test_strtk($TestName)

     {

            //调用父类的构造函数以执行名为$TestName的测试方法

            $this->PHPUnit_TestCase($TestName);

     }

    

     //initialization

     function setUp()

     {

            //每个测试方法被执行之前此函数都会被调用

     }

    

     //depose

     function teardown()

     {

            //每个测试方法被执行之后此函数都会被调用

     }

    

     //第一个测试方法:无匹配字符,应返回原串

     function test_no_match()

     {

            $teststr="abcd";

            $rslt=strtk($teststr,"#");

            $this->assertEquals($teststr,$rslt,"无匹配字符时未返回原串");

     }

}

 

//执行指定的测试用例类中每一个名字以'test'开头的方法

//此时测试结果输出时将统一小写,故方法命名中用'_'

$tsStrtk=new PHPUnit_TestSuite(test_strtk);

$TestRslt=PHPUnit::run($tsStrtk);//执行获得结果

echo $TestRslt->toHTML();//结果在浏览器中输出

?>

保存为test_strtk.php,执行得到结果:

Fatal error: Failed opening required 'strtk.php'

意料之中,因为要被测试的文件和函数都还不存在。现在测试要求我们创建文件strtk.php并编写函数strtk()

//strtk.php

function strtk($str,$sep)

{

     return $str;//Fake-It Pattern

}

?>

执行test_strtk.php,测试通过:

TestCase test_strtk->test_no_match() passed

TDD中,最重要的是尽快让测试通过。直接返回$str看起来似乎不可理喻,但确实是让测试最快通过的方法。这正符合TDD中的“伪实现模式(Fake-It Pattern)”。伪实现马上就会因为测试的增加而被替换掉。但是为什么不直接硬编码返回”abcd”呢?因为返回$str这个显而易见的抽象在实际中并不耗费我们比硬编码更多的时间。当抽象不那么轻松的时候,我们还有下面的方法来用测试驱动抽象的必要性,保证抽象的正确性。

3.3第二轮 —— 三角模式

调用一次strtk()应该能获得被分割的第一个子串。我们在test_strtk类中增加测试方法:

     //获得第一个子串

     function test_get_first_substr()

     {

            $teststr="one,two,three";

            $rslt=strtk($teststr,",");

            $this->assertEquals("one",$rslt,"未能获得第一个子串");

            //Triangulate Pattern

            $teststr="Feb./8/2004";

            $rslt=strtk($teststr,"/");

            $this->assertEquals("Feb.",$rslt,"未能获得第一个子串");

     }

运行测试,和预计的一样得到失败的结果:

TestCase test_strtk->test_no_match() passed

TestCase test_strtk->test_get_first_substr() failed: 未能获得第一个子串 expected one, actual one,two,three

TestCase test_strtk->test_get_first_substr() failed: 未能获得第一个子串 expected Feb., actual Feb./8/2004

直接返回原串行不通了。通过测试实践还可以证明尝试硬编码返回”one”或者”Feb.”也都不行。这个测试方法中同时存在两个断言,利用了“三角模式(Triangulate Pattern)”提出了进一步抽象的需要。测试要求我们利用explode()函数了:


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
php学习之道:mysqlSELECTFOUND_ROWS()与COUNT(*)使用方法差别发布时间:2022-07-10
下一篇:
centosyum安装mongodb以及php扩展发布时间:2022-07-10
热门推荐
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap