用户
 找回密码
 入住 CI 中国社区
搜索
查看: 13138|回复: 9
收起左侧

[Others] CI 的 单 元 测 试

[复制链接]
发表于 2013-6-20 10:41:22 | 显示全部楼层 |阅读模式
      最近几周一直在学习CI框架,利用CI框架可以方便快速地建议自己的网站。而CI还有一个意思就是持续化集成,所以就要求测试也自动化。但是我搜了一下,这方面的文档不多,实在没有办法,就把CI与单元测试相关的源码给全看了一下,虽然不是太懂,但是已经可以自己写测试用例了,现在总结一下,希望对大家有所帮助。
初步接触CI的单元测试
      CI是利用PHPUNIT进行测试的,而框架里面自带的也有写好的测试用例,大家可以拿来练一下手.
(1)安装PHPUNIT,具体安装方法,在不同的操作系统下也不相同,网上有很多方法,可以参照一下。安装完成后会显示下面的效果,说明安装成功:
   C:\>phpunit --version
PHPUnit 3.6.11 bySebastian Bergmann.
  2)进入你放置CI代码下的tests文件夹下,运行phpunit命令。
D:\DinerSystem\Sinafood\tests>phpunit
PHPUnit3.6.11 by Sebastian Bergmann.
Configurationread from D:\DinerSystem\Sinafood\tests\phpunit.xml
..................
Time:1 second, Memory: 4.00Mb
[30;42m[2KOK(18 tests, 24 assertions)
[0m[2K
D:\DinerSystem\Sinafood\tests>
        你运行结果中的testsassertions个数可能和我的不一样,因为我现在的结果加了其他的测试用例,只要出来这个结果,说明测试可以进行了。
CI单元测试结构介绍
(1)测试文件结构
在框架根目录下的tests文件夹中有以下几个文件夹,其作用如下:
Controllers:存放对Application中的controller中的文件进行测试。
Fixtures:存放测试数据文件yml及其他测试(暂时没有搞明白)。
Helpers:存放对Application中的helpers中的文件进行测试。
Libs:存放对Application中的libs中的文件进行测试。
Models:存放对Application中的models中的文件进行测试。
System:存放对Application中的system中的文件进行测试。
该文件夹下还有如generate,generate.php, getops.php, holder 及phpunit.xml在写测试用例的时候我们不用修改,暂忽略之。
在application文件夹下还有一个third_party的文件夹,这个里面有CIUnit文件夹,里面存放着生成和回收测试数据的Fixture.php,还有其他进行测试相关驱动的文件,目前我没有研究它们的工作方法。
(2)测试数据库
如果你要测试的程序有数据操作的话,在测试之间要先往数据库中存放一些儿测试数据,测试完成之后要清除数据,不过这些儿工作CI框架已经帮你完成,你只要建测试数据库及对应的表就行了。测试数据库必须以“­_test”结尾,其连接配置在:application/config/testing/database.php中设置,方法和CI的正常配置一样。如果在文件中配置了$db['default']['dbprefix']= 'ff_';项,则要注意:测试表必须以“ff_”开头。
测试数据存放在tests/fixtures文件夹中,测试文件以”原项目中的表名_fixt.yml”命名,否则程序无法读取其中的数据。Yml文件中的数据格式如下:
现有测试数据表:
SQL复制代码
ff_phone_carrier([/align][align=LEFT]name     VARCHAR(255)[/align][align=LEFT]txt_address    VARCHAR(255)[/align][align=LEFT]   txt_message_length      INT(11));[/align]              
复制代码

很显然原工程中的表名为:phone_carrier,现在测试数据文件中:phone_carrier_fixt.yml:
1:                         //此为第一条记录
    name: 'Verizon'          //对应字段的值
    txt_address: '@vzpix.com'
    txt_message_length: 160
2:
    name: 'AT&T'
    txt_address: '@mms.att.net'
    txt_message_length: 160
(3)测试用例结构
A,Controller测试用例结构
PHP复制代码
<?php[/i][/align][align=left][i]/**
* @group Controller
*/[/i][/align][align=left][i]class SomeControllerTest extendsCIUnit_TestCase[/i][/align][align=left][i]{[/i][/align][align=left][i]  publicfunction setUp()[/i][/align][align=left][i]  {[/i][/align][align=left][i]      //Set the tested controller
     $this->CI= set_controller('welcome');//设置要测试的controller,本例为welcome
 }
 
 publicfunction testWelcomeController()
 {   // 调用被测试的Controller中的方法 index()
     $this->CI->index();
     //获得输出结果
     $out= output();        
     //对结果进行检测
     $this->assertSame(0,preg_match('/(error|notice)/i', $out));
 }
}
复制代码

B,Model测试用例结构:
PHP复制代码
 
<?php[/i][/align][align=left][i]/**
* @group Model
*/[/i][/align][align=left][i]class PhoneCarrierModelTest extendsCIUnit_TestCase[/i][/align][align=left][i]{[/i][/align][align=left][i]  protected$tables = array([/i][/align][align=left][i]      'ff_phone_carrier'=> 'phone_carrier'  //设置数据表:测试用例表对=>正式表
 );  
 private$_pcm;
 
 publicfunction __construct($name = NULL, array $data = array(), $dataName = '')
 {
     parent::__construct($name,$data, $dataName);
 }
 
 publicfunction setUp()
 {//测试开始执行的方法
     parent::setUp();        
     /*
     引用要测试的model,并将测试数据写入测试库的对应该表中。
     */      
     $this->CI->load->model('Phone_carrier_model');
     $this->_pcm= $this->CI->Phone_carrier_model;
 }
 
 publicfunction tearDown()
 {//测试结束执行的方法
     parent::tearDown();
 }
 
 //------------------------------------------------------------------------
 
 /**
  * @dataProvider GetCarriersProvider  //设置数据提供者
  */
 publicfunction testGetCarriers(array $attributes, $expected)
 {   //具体的测试方法
     $actual= $this->_pcm->getCarriers($attributes);        
     $this->assertEquals($expected,count($actual));
 }
 
 publicfunction GetCarriersProvider()
 {//提供检测数据的方法,也可以直接写数据
     returnarray(
         array(array('name'),5)
     );
 }  
}
复制代码
实战演习
1,要测试的项目
现有一小网页显示数据库foodfeed中的一个表单food中的数据,各个文件内容如下:
(1) Controller :showmenu.php
PHP复制代码
 
<?php[/align][align=left]class showmenu extends CI_Controller {[/align][align=left]        public function __construct()[/align][align=left]       {[/align][align=left]                   parent::__construct();[/align][align=left]                 $this->load->model(showmenu _m');
      }        
      //查询显示所有数据
      public function index()
      {
            $data['query']=$this-> showmenu _m->getfoodlist();
            $this->load->view(showmenuview,$data);
      }
}
?>
复制代码

(2)model: showmenu_m.php
PHP复制代码
 
<?php[/align][align=left]class showmenu _m extends CI_Model [/align][align=left]{   [/align][align=left]   function __construct()[/align][align=left]    {[/align][align=left]      $this->load->database();[/align][align=left]     }[/align][align=left]    //获取food表中的所有记录
  function getfoodlist()
  {            
      $this->db->select('*')->from('food');
        $query= $this->db->get();      
      return $query->result();
   }
}
?>
复制代码

(3)view:showmenuview.php
PHP复制代码
 
<htmlxmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type"content="text/html; charset=utf-8" />
<title>推荐菜品展示</title>
</head>
  <body>
<div>
      <table border=1>
          <tr>
               <td>菜品ID</td>
               <td>菜品名称</td>
                <td>单价</td>
               <td>餐馆</td>
               <td>数量</td>
          </tr>          
       <?php foreach ($query as $rows):?>
          <tr>                  
              <td><?=$rows->id?></td>
              <td><?=$rows->name?></td>
              <td><?=$rows->price?></td>
              <td><?=$rows->owner?></td>
               <td><?=$rows->count?></td>                
          </tr>
         <?php endforeach;?>
      </table>
  </div>  
</body>
</html>
复制代码

(4)程序所用的数据表结构如下:
Foodfeed.food(
id        int(11),
name  varchar(45),
owner         varchar(45),
price   int(11),
count  int(11) );
2,测试战争
1)建立测试数据库foodfeed_test,对应的表:ff_food,表结构同正式表,表中无任何数据。
2)在tests/fixtures中建测试数据文件food_fixt.yml,内容如下:
## YAML Template.
1:
id: 1
name: "鱼香肉丝"
owner: "面香"
price: 15
count: 12
2:
id: 2
name: "红烧带鱼"
owner: "大食代"
price: 28
count: 1
3)创建测试文件
   A,controller的测试:
PHP复制代码
 
<?php[/align][align=left]class ShowMenuTestextends CIUnit_TestCase[/align][align=left]{       [/align][align=left]       protected $tables = array([/align][align=left]                   'ff_food' => 'food'  //设置表对应关系
            );          
        private $_pcm;
       
        public function __construct($name =NULL, array $data = array(), $dataName = '')
        {
                  parent::__construct($name,$data, $dataName);
        }
   public function setUp()
        {  //装载测试数据
       parent::setUp();
       $this->CI->load->model('showmenu_m');
                  $this->_pcm =$this->CI-> showmenu_m;
                  // Set the tested controller
                  $this->CI =set_controller(“showmenu”);
        }
       
       //测试showmenuindex()
        public function testIndexController()
        {          
                  // Call the controllersmethod
                  $this->CI->index();            
                  // Fetch the buffered output
                  $out = output();
       // Check if the content is OK
           preg_match_all("/<td>(.*)<\/td>/isU",$out,$arr);  //对返回的数据进行筛选
       //print_r($arr);
       $this->assertSame('15',$arr[1][7]);//判断筛选的结果
       $this->assertSame('28',$arr[1][37]);            
           
        }      
}
?>
复制代码

B,model进行测试
PHP复制代码
 
<?php[/size][/font][/align][align=left][font=&quot][size=10.5pt]class ShowMenuModelTest extendsCIUnit_TestCase[/size][/font][/align][align=left][font=&quot][size=10.5pt]{[/size][/font][/align][align=left][font=&quot][size=10.5pt]      protected $tables = array([/size][/font][/align][align=left][font=&quot][size=10.5pt]              'ff_food'=> 'food'[/size][/font][/align][align=left][font=&quot][size=10.5pt]       );          [/size][/font][/align][align=left][font=&quot][size=10.5pt]       private$_pcm;[/size][/font][/align][align=left][font=&quot][size=10.5pt]       [/size][/font][/align][align=left][font=&quot][size=10.5pt]       publicfunction __construct($name = NULL, array $data = array(), $dataName = '')[/size][/font][/align][align=left][font=&quot][size=10.5pt]       {[/size][/font][/align][align=left][font=&quot][size=10.5pt]              parent::__construct($name,$data, $dataName);[/size][/font][/align][align=left][font=&quot][size=10.5pt]       }[/size][/font][/align][align=left][font=&quot][size=10.5pt]       [/size][/font][/align][align=left][font=&quot][size=10.5pt]       publicfunction setUp()[/size][/font][/align][align=left][font=&quot][size=10.5pt]       {//装载测试数据
             parent::setUp();            
             $this->CI->load->model('recommand_m');
             $this->_pcm= $this->CI->recommand_m;
      }    
      publicfunction tearDown()
      {
             parent::tearDown();
      }          
      //测试全部数据显示
      public function testmodelindex()
      {      
       $actual = $this->_pcm->getfoodlist( );        
           $this->assertEquals(15,$actual[0]->price);//检测返回的结果
       $this->assertEquals(28, $actual[6]->price);
             
     }    
   }
?>
复制代码

(4)运行测试用例
D:\DinerSystem\Sinafood\tests>phpunit
PHPUnit 3.6.11 by Sebastian Bergmann.
Configuration read fromD:\DinerSystem\Sinafood\tests\phpunit.xml
..................
Time: 4 seconds, Memory: 4.00Mb
[30;42m[2KOK (18 tests, 24 assertions)
[0m[2K
D:\DinerSystem\Sinafood\tests>
(5)排查测试中存在的错误
        测试用例在运行过程中会出现各种错误的情况,这个时候就需要你对其进行排查。我建议:(a)对要检测的结果进行输出,以方便知道哪儿发生了错误。(b)程序中使用CI框架标准的数据操作函数,否则也会出错。其他的情况也会非常多,就不一一列举了,具体问题具体分析吧!

 楼主| 发表于 2013-6-20 10:44:07 | 显示全部楼层
,我设置成PHP代码后,程序咋这么乱呢?
发表于 2013-6-20 13:58:11 | 显示全部楼层
清道夫 发表于 2013-6-20 10:44
,我设置成PHP代码后,程序咋这么乱呢?

把代码标签里的其他标签都去掉就行了。
 楼主| 发表于 2013-6-20 14:03:16 | 显示全部楼层
Hex 发表于 2013-6-20 13:58
把代码标签里的其他标签都去掉就行了。

发过的帖子不能修改啊?
发表于 2013-6-20 14:07:33 | 显示全部楼层
清道夫 发表于 2013-6-20 14:03
发过的帖子不能修改啊?

可以修改吧,你没有编辑按钮吗?
 楼主| 发表于 2013-6-20 14:40:31 | 显示全部楼层
Hex 发表于 2013-6-20 14:07
可以修改吧,你没有编辑按钮吗?

没有,大概有等级限制吧?
发表于 2013-7-1 16:49:43 | 显示全部楼层
大哥,改下格式把,好乱
 楼主| 发表于 2013-7-2 11:23:58 | 显示全部楼层
★♂翼☆ 发表于 2013-7-1 16:49
大哥,改下格式把,好乱

我没有编辑权限啊!!

发表于 2013-8-6 13:02:34 | 显示全部楼层
看起来好强,先Mark!
发表于 2014-10-10 11:53:44 | 显示全部楼层
楼主,我想问个问题,我在测试我的model层代码,但是一些可以正常运行,一些显示加载失败:[CIUnit] Error: 500 Message: Unable to locate the model you have specified: ordercommentcomplaint。您知道是什么问题么?不用ciunit测试我的代码是完全可以跑通的。一用ciunit就加载不成功了。

本版积分规则