欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

php单元测试使用示例(转载) 博客分类: PHP php单元测试 

程序员文章站 2024-02-16 15:15:10
...
转载自: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 );
        }
}

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

相关标签: php 单元测试