`
xieye
  • 浏览: 834863 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

php单元测试使用示例(转载)

    博客分类:
  • PHP
阅读更多
转载自:http://www.javaranch.com/unit-testing.jsp,原文标题《Evil Unit Testing》,原作者:Paul Wheaton
转载自: http://www.cnblogs.com/harlanc/p/6838155.html ,原文标题《单元测试有毒》翻译者:HarlanC
我稍做了修改。

作者的核心观点是:绝大部分的项目,应该做的测试是,单元测试70%,其余才是功能测试和集成测试。单元测试是基础。
作者的观点:
单元测试是可测试代码的最小的一部分。通常是一个单一的方法,不会使用其它方法或者类。非常快!上千个单元测试能够在10秒以内跑完!单元测试永远不会使用:

  •     数据库
  •     一个app服务器(或者任何类型的服务器)
  •     文件/网络 I/O或者文件系统
  •     另外的应用
  •     控制台(System.out,system.err等等)
  •     日志
  •     大多数其他类(但不包括DTO‘s,String,Integer,mock和一些其他的类)


单元测试几乎总是回归测试套件(regression suite)的一部分。

下面是单元测试使用示例:
假设被测类FarmServlet如下:

//原被测类
public class FarmServlet extends ActionServlet
{ 
        public function doAction( ServletData $servletData ) 
        {
            $species = $servletData->getParameter("species");
            $buildingID = $servletData->getParameter("buildingID");
            if ( Str::usable( $species ) && Str::usable( $buildingID ) )
            {
                $remote = FarmEJBUtil::getHome()->create();
                $remote->addAnimal( $species , $buildingID );
            }
        } 
}

这里不仅仅调用了其他业务逻辑,还调用了应用服务器!可能还会访问网络!上千次的调用可能会花费不少于10秒的时间。另外对$remote的修改可能会破坏我对这个方法的测试!所以我们需要引入一个mock对象。

首先是创建mock。如果$remote是一个对象,我将会继承(extend)它并且重写(override)或实现(implement)所有的方法。

//远程服务对象mock类,
//新写的第一个类,为了单元测试
public class MockRemote 
{
        $addAnimal_species = null;
        $addAnimal_buildingID = null;
        $addAnimal_calls = 0;
        public function addAnimal( $species, $buildingID )
        {
            $this->addAnimal_species = $species ;
            $this->addAnimal_buildingID = $buildingID ;
            $this->addAnimal_calls++;
        }
}


这个MockRemote类什么都没做,只是携带了单元测试和需要被测试代码之间要交互的数据。

现在我需要使用mock代码来替代调用应用服务器的部分。我对需要使用mock的地方做了注释:

首先,被测类FarmServlet中,让我们把这句代码从其他猛兽中分离出来:

//修改后的原被测类,为了单元测试,以及代码解耦。
public class FarmServlet extends ActionServlet
{
        // 新加公共的成员方法,可以让测试类注入或通过继承修改。
        public function getRemote()  
        { 
            return FarmEJBUtil::getHome()->create(); 
        }

        public function doAction( ServletData $servletData ) 
        {
            $species = $servletData->getParameter("species");
            $buildingID = $servletData->getParameter("buildingID");
            if ( Str::usable( $species ) && Str::usable( $buildingID ) )  {
                $remote = $this->getRomote();  // 改成从公共成员方法中获取对象
                $remote->addAnimal( $species , $buildingID );
            }
         }
}


前面已经新写了一个mock类,现在再写一个类FarmServletShunt继承被测类,然后对继承的这个类做测试,成功即认为原先的被测类功能正确。
//该类FarmServletShunt继承被测类,测试用
// 新写的第2个类
class FarmServletShunt extends FarmServlet
{ 
        puhblic $getRemote_return; // 新加成员变量,好注入
        public function getRemote() // 覆盖了被测类的获得远程服务的方法,实现注入
        {
            return getRemote_return;
        }
}

注意一下怪异的名字:“shunt”。我不确定它是什么意思,但我认为这个词语来自电子工程/工艺,它指用一段电线来临时组装一个完整的电路。一开始听起来这个想法很愚蠢,但是过后我就慢慢习惯了。

一个shunt有点像mock,一个没有重写所有方法的mock。用这种方法,你可以mock一些方法,然后测试其他的方法。一个单元测试可以由几个shunts来完成,它们重写了相同的类,每个shunt测试了类的不同部分。Shunt通常情况下为嵌套类。


再对ServletData做mock,方便注入。

//对ServletData类或接口做mock
//新写的第3个类,为了单元测试
public class MockServletData 
{

        private $arr;

        public function __construct($arr){

            $this->arr = $arr;

        }

        public function getParameter( $parameter )
        {
            return $this->arr[$paramter];
        }
}

终场表演的时候到了!看一下单元测试代码!

// 这是最后写的测试文件,
public class TestFarmServlet extends TestCase
{
        public function testAddAnimal() {
            $mockRemote = new MockRemote();//定义mock的远程服务对象
            $shunt = new FarmServletShunt();//该类继承被测类,拿它测试
            $shunt->getRemote_return = $mockRemote; //注入远程服务对象
            // 另一个mock对象
            $mockServletData = new MockServletData([      // 定义mock的ServletData对象
                "species" => "dog",
                "buildingID" => 27,
            ]); 

            // 执行被测方法
            $shunt->doAction( $mockServletData );

            // 断言原被测类中,远程对象使用了一次addAnimal方法
            $this->assertEquals( 1 , $mockRemote->addAnimal_calls );

            //个人感觉下面两句不重要。因为这里的逻辑其实是mock里的逻辑。
            $this->assertEquals( "dog" , $mockRemote->addAnimal_species );
            $this->assertEquals( 27 ,    $mockRemote->addAnimal_buildingID );
        }
}

基本的测试框架我们就展示完了。下面我要和大家分享一个和单元测试有关的概念——依赖注入,也是我们的单元测试中要到的,敬请期待。

分享到:
评论

相关推荐

    sbupl 纯HTML5+JS批量上传 带PHP示例仅2KB

    ... 代码编写参考了IBM、CSDN、博客园上的部分文章,在此感谢。 文件大小只有2KB,无需flash、jquery、FileAPI等额外...各位按需随意修改,切勿直接使用示例。转载望注明出处,谢谢大家! 欢迎访问:http://qiukong.com

    Vedio.class_soe8b_网易云视频直播apiPHP源码转载_直播接口_

    【标题】"Vedio.class_soe8b_网易云视频直播apiPHP源码转载_直播接口_" 提供的信息表明,这是一个关于使用PHP处理网易云视频直播API的代码示例。这个类文件,Vedio.class.php,很可能是用来与网易云信的直播服务进行...

    PHP实用指南 0.23

    <br>4、示例中用到很多的函数,看一点就不得不查阅一下涉及到的函数的意思 <br>5、很多资料讲授的方法,在最新的PHP中已经不建议使用了,甚至已经被废除! <br> <br>其实新手最需要的是: <br>1、...

    贴图库+3 纯HTML5贴图库批量上传

    HTML5+JS贴图库批量上传示例。 相比swfupload:无需Flash组件支持,支持手机,节省内存。 相比Plupload:无需jQuery,代码仅2KB,轻...各位按需随意修改,转载请注明作者博客,谢谢大家~ 欢迎访问:http://qiukong.com

    贴图库+2 纯HTML5贴图库批量上传

    HTML5+JS贴图库批量上传示例。 相比swfupload:无需Flash组件支持,支持手机,节省内存。 相比Plupload:无需jQuery,代码仅2KB,轻...各位按需随意修改,转载请注明作者博客,谢谢大家~ 欢迎访问:http://qiukong.com

    tietukuplus 纯HTML5贴图库批量上传

    HTML5+JS贴图库批量上传示例。 相比swfupload:无需Flash组件支持,支持手机,节省内存。 相比Plupload:无需jQuery,代码仅2KB,轻...各位按需随意修改,转载请注明作者博客,谢谢大家~ 欢迎访问:http://qiukong.com

    sbupl4tietuku2 纯HTML5贴图库批量上传 无需中转服务器

    使用方法: 1.到 http://open.tietuku.com/createtoken 申请上传密钥。 2.用notepad++等编辑index.html,按说明填写密钥。 3.打开index.html即可进行直传测试。 各位按需随意修改,转载请注明作者博客,谢谢大家~ ...

    sbupl4tietuku 纯HTML5贴图库批量上传 无需中转服务器

    使用方法: 1.到 http://open.tietuku.com/createtoken 申请上传密钥。 2.用notepad++等编辑index.html,填入token='你的密钥'。 3.打开index.html即可进行直传测试。 各位按需随意修改,转载请注明作者博客,谢谢...

    sbupl4tietuku3 纯HTML5贴图库批量上传 无需中转服务器

    HTML5+JS贴图库批量上传示例,无需jQuery、Flash、中转服务器等。 相比PHPSDK:无需中转,省去本地资源消耗。 相比Plupload:无需...各位按需随意修改,转载请注明作者博客,谢谢大家~ 欢迎访问:http://qiukong.com

    贴图库+ 纯HTML5贴图库批量上传

    HTML5+JS贴图库批量上传示例,无需jQuery、Flash、中转服务器。 相比PHPSDK:无需中转,省去本地资源消耗。 相比Plupload:无需...各位按需随意修改,转载请注明作者博客,谢谢大家~ 欢迎访问:http://qiukong.com

    2014年辛星ThinkPHP教程夏季版

    从数据库建模、模块建立、整合测试到最终的总结,整个过程通过实际操作帮助读者理解ThinkPHP的开发流程和实际应用。 教程的特色是注重实用性和更新性,作者辛星希望借助此教程来提供更多的帮助,并期望得到读者的...

    risk_engine:风控决策引擎系统

    风控决策引擎系统 决策引擎系统介绍 风控决策引擎系统是在大数据...本项目有用学习和参考,不能直接用于生产环境,转载使用请说明出处。代码不定期重复更新,可加关注查看。如有交流欢迎加微信号hepenggj 服务测试 gi

    Ajax 模仿google百度提示输入框 v1.8

    *****************************以下是调用示例*************************: var option = { inputName : "test2", inputText : "测试字符串4", inputValue: "我是值", inputlength:1, url : "search.asp", arrow :1,...

    飞飞Ajax模仿google提示输入框源码 v1.7

    版本所提的修改建议*************************************************飞飞Asp乐园www.ffasp.comQQ:276230416QQ:72840059*********************************************转载请注明出处此小程序是模仿Google...

Global site tag (gtag.js) - Google Analytics