日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
PHP單元測(cè)試?yán)鳎篜HPUnit深入理解

在本系列文章的前兩篇中初探PHP單元測(cè)試?yán)鳎篜HPUnit和PHP單元測(cè)試?yán)鳎篜HPUnit深入用法中,分別介紹了PHPUnit的基本用法和PHPUnit中的一些重要用法。在本文中,筆者將為大家介紹PHPUnit中的兩個(gè)高級(jí)概念和用法,盡管它不一定在你的日常單元測(cè)試中都用到,但理解和學(xué)會(huì)它們的用法對(duì)學(xué)習(xí)PHPUnit還是十分重要的。

創(chuàng)新互聯(lián)公司堅(jiān)持“要么做到,要么別承諾”的工作理念,服務(wù)領(lǐng)域包括:成都做網(wǎng)站、成都網(wǎng)站建設(shè)、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣等服務(wù),滿足客戶于互聯(lián)網(wǎng)時(shí)代的旌陽(yáng)網(wǎng)站設(shè)計(jì)、移動(dòng)媒體設(shè)計(jì)的需求,幫助企業(yè)找到有效的互聯(lián)網(wǎng)解決方案。努力成為您成熟可靠的網(wǎng)絡(luò)建設(shè)合作伙伴!

PHPUnit中的Annotations

如果有其他編程語(yǔ)言經(jīng)驗(yàn)的開發(fā)者,應(yīng)該對(duì)Annotations(注解)不陌生,其實(shí)在PHPUnit中,一個(gè)簡(jiǎn)單的如下面的一段注釋也可以認(rèn)為是Annotations:

 
 
 
  1. class MyTestClass extends PHPUnit_Framework_TestCase  
  2. {  
  3. /**  
  4. * Testing the answer to “do you love unit tests?”  
  5. */ 
  6. public function testDoYouLoveUnitTests()  
  7. {  
  8. $love = true;  
  9. $this->assertTrue($love);  
  10. }  
  11. }  
  12. ?> 

可以看到,其實(shí)一段以/** **/為標(biāo)記的文字,就可以認(rèn)為是一種Annotations,但Annotations其實(shí)不單單是簡(jiǎn)單的注釋,它是與一個(gè)程序元素相關(guān)聯(lián)信息或者元數(shù)據(jù)的標(biāo)注,它不影響程序的運(yùn)行,但相關(guān)的軟件工具或框架能夠?qū)⑵滢D(zhuǎn)換成特殊的元數(shù)據(jù)標(biāo)記,以方便開發(fā)者以更少的代碼去提高效率(比如通過(guò)。如果你熟悉Java,則會(huì)發(fā)現(xiàn)在Java SE 5中及象Spring等框架中,都大量使用了Annotations。

然而,由于PHP并不象Java那樣是編譯性語(yǔ)言,因此本身缺乏去解析Annotations的機(jī)制,但幸好PHPUnit去提供了這樣的功能,我們以下面的代碼為例:

 
 
 
  1. class MyMathClass  
  2. {  
  3. /**  
  4. * Add two given values together and return sum  
  5. */ 
  6. public function addValues($a,$b)  
  7. {  
  8. return $a+$b;  
  9. }  
  10. }  
  11. ?> 

上面的只是一個(gè)簡(jiǎn)單的加法的例子,為此,我們使用Annotations去編寫一個(gè)單元測(cè)試,在上兩篇文章中,我們采用的是手工編寫單元測(cè)試的方法,而本文中,將介紹使用PHPUnit命令行的方法,自動(dòng)生成單元測(cè)試的框架,方法如下:

首先把上面的類保存為MyMathClass.PHP,然后在命令行下運(yùn)行如下命令:

 
 
 
  1. phpunit –skeleton-test MyMathClass 

這時(shí)PHPUnit會(huì)自動(dòng)生成如下的框架單元測(cè)試代碼:

 
 
 
  1. require_once '/path/to/MyMathClass.php';  
  2. /**  
  3. * Test class for MyMathClass.  
  4. * Generated by PHPUnit on 2011-02-07 at 12:22:07.  
  5. */ 
  6. class MyMathClassTest extends PHPUnit_Framework_TestCase  
  7. {  
  8. /**  
  9. * @var MyMathClass  
  10. */ 
  11. protected $object;  
  12. /**  
  13. * Sets up the fixture, for example, opens a network connection.  
  14. * This method is called before a test is executed.  
  15. */ 
  16. protected function setUp()  
  17. {  
  18. $this->object = new MyMathClass;  
  19. }  
  20. /**  
  21. * Tears down the fixture, for example, closes a network connection.  
  22. * This method is called after a test is executed.  
  23. */ 
  24. protected function tearDown()  
  25. {  
  26. }  
  27. /**  
  28. * @todo Implement testAddValues().  
  29. */ 
  30. public function testAddValues()  
  31. {  
  32. // Remove the following lines when you implement this test.  
  33. $this->markTestIncomplete(  
  34. 'This test has not been implemented yet.' 
  35. );  
  36. }  
  37. }  
  38. ?> 

可以看到,PHPUnit為我們生成的單元測(cè)試代碼自動(dòng)引入了原來(lái)的MyMathClass.PHP,同時(shí)也生成了setUp和tearDown方法,但唯一的核心單元測(cè)試代碼是留給了我們編寫。如果想在這個(gè)基礎(chǔ)上更快速的生成我們想要的單元測(cè)試代碼,要如何實(shí)現(xiàn)呢?沒(méi)錯(cuò),就是使用annotations!我們可以在原來(lái)的MyMathClass.PHP中加入如下的annotations。

 
 
 
  1. class MyMathClass  
  2. {  
  3. /**  
  4. * Add two given values together and return sum  
  5. * @assert (1,2) == 3  
  6. */ 
  7. public function addValues($a,$b)  
  8. {  
  9. return $a+$b;  
  10. }  
  11. }  
  12. ?>  

然后再象上述一樣在命令行運(yùn)行:

 
 
 
  1. phpunit –skeleton-test MyMathClass 

這個(gè)時(shí)候會(huì)為我們生成如下的單元測(cè)試代碼:

 
 
 
  1. /**  
  2. * Generated from @assert (1,2) == 3.  
  3. */ 
  4. public function testAddValues()  
  5. {  
  6. $this->assertEquals(  
  7. 3,  
  8. $this->object->addValues(1,2)  
  9. );  
  10. }  
  11. ?> 

看到了么?我們?cè)谠械念愔屑尤肓俗⒔釦assert(1,2)==3,則PHPUnit自動(dòng)為我們生成了正確的單元測(cè)試代碼。當(dāng)然,可以參考PHPUnit手冊(cè),學(xué)習(xí)到更多的關(guān)于@assert注解使用的規(guī)則。

下面再舉一個(gè)例子來(lái)講解annotations。假設(shè)我們的程序中的一個(gè)方法,只是僅需要數(shù)據(jù)的輸入,并且不依賴XML或者數(shù)據(jù)庫(kù)提供數(shù)據(jù)源,則為了測(cè)試這個(gè)方法,我們可能想到的一個(gè)方法是在程序中設(shè)置一個(gè)測(cè)試數(shù)據(jù)集去測(cè)試,但這里介紹一個(gè)比較簡(jiǎn)單的方法,就是使用注解@dataProvider,修改MyMathClass.PHP如下:

 
 
 
  1. /**  
  2. * Data provider for test methods below  
  3. */ 
  4. public static function provider()  
  5. {  
  6. return array(  
  7. array(1,2,3),  
  8. array(4,2,6),  
  9. array(1,5,7)  
  10. );  
  11. }  
  12. /**  
  13. * Testing addValues returns sum of two values  
  14. * @dataProvider provider  
  15. */ 
  16. public function testAddValues($a,$b,$sum)  
  17. {  
  18. $this->assertEquals(  
  19. $sum,  
  20. $this->object->addValues($a,$b)  
  21. );  
  22. }  
  23. ?> 

可以看到,這里使用了注解@dataProvider,指明了測(cè)試用例的數(shù)據(jù)提供者是由provider方法返回的一個(gè)數(shù)組。所以在單元測(cè)試時(shí),數(shù)組中的第0個(gè)元素則會(huì)賦值給$a,第1個(gè)元素則會(huì)賦值給b,第3個(gè)元素則會(huì)賦值給sum,可以看到,上面的第3個(gè)數(shù)組提供的數(shù)據(jù)是不能通過(guò)單元測(cè)試的,因?yàn)?+5不等于7。

此外,這里還簡(jiǎn)單介紹兩個(gè)常用的annotations,比如@expectedException注解可以測(cè)試代碼中是否正確拋出了異常,比如:

 
 
 
  1. class ExceptionTest extends PHPUnit_Framework_TestCase{      
  2. /**    
  3.    * @expectedException InvalidArgumentException     */      
  4. public function testException()    {    
  5.   }  
  6. }  
  7.  
  8. ?>  

這里就用注解的方法表示testException中必須拋出的異常類型為InvalidArgumentException。

另外一個(gè)是@cover注解。它的作用是標(biāo)識(shí)PHPUnit只為類中的哪些方法或作用域生成測(cè)試代碼,比如:

 
 
 
  1. /**  
  2.      * @covers SampleClass::publicMethod  
  3.      * @covers SampleClass::  
  4.      * @covers HelperClass  
  5.      */ 
  6.     public function testMethod()  
  7.     {  
  8.         $result = SampleClass::method();  

則PHPUnit只為SampleClass類中的publicMethod方法、SampleClass類中的所有非public聲明的方法和HelperClass類或者它的其中一個(gè)父類產(chǎn)生單元測(cè)試代碼。

#p#

PHPUnit中的Mocking

在介紹Mocking前,先來(lái)看下為什么要使用Mocking。舉一個(gè)數(shù)據(jù)庫(kù)查詢的例子,比如在某個(gè)應(yīng)用中,如果要測(cè)試一個(gè)數(shù)據(jù)庫(kù)的應(yīng)用,但假如這個(gè)數(shù)據(jù)庫(kù)的測(cè)試要耗費(fèi)很多資源以及編寫很復(fù)雜的單元測(cè)試的代碼的話,可以嘗試使用Mocking技術(shù)。舉例說(shuō)明如下:

 
 
 
  1. class Database  
  2. {  
  3. public function reallyLongTime()  
  4. {  
  5. $results = array(  
  6. array(1,'test','foo value')  
  7. );  
  8. sleep(100);  
  9. return $results;  
  10. }  
  11. }  
  12. ?> 

在上面這個(gè)例子中,我們模擬了一個(gè)數(shù)據(jù)庫(kù)的操作,認(rèn)為它需要運(yùn)行很長(zhǎng)時(shí)間。接下來(lái)我們編寫其單元測(cè)試代碼如下:

 
 
 
  1. require_once '/path/to/Database.php';  
  2. class DatabaseTest extends PHPUnit_Framework_TestCase  
  3. {  
  4. private $db = null;  
  5. public function setUp()  
  6. {  
  7. $this->db = new Database();  
  8. }  
  9. public function tearDown()  
  10. {  
  11. unset($this->db);  
  12. }  
  13. /**  
  14. * Test that the "really long query" always returns values  
  15. */ 
  16. public function testReallyLongReturn()  
  17. {  
  18. $mock = $this->getMock('Database');  
  19. $result = array(  
  20. array(1,'foo','bar test')  
  21. );  
  22. $mock->expects($this->any())  
  23. ->method('reallyLongTime')  
  24. ->will($this->returnValue($result));  
  25. $return = $mock->reallyLongTime();  
  26. $this->assertGreaterThan(0,count($return));  
  27. }  
  28. }  
  29. ?>  

注意看這段代碼中有趣的地方,這里,使用了PHPUnit中的getMock對(duì)象方法,這里實(shí)際上是模擬生成一個(gè)Database類的“偽實(shí)例”了,這里生成了$mock這個(gè)mock對(duì)象實(shí)例,以方便接著的單元測(cè)試中用到。接下來(lái)的這三行代碼:

 
 
 
  1.  $mock->expects($this->any())  
  2. ->method('reallyLongTime')  
  3. ->will($this->returnValue($result)); 

它們的含義為:無(wú)論方法reallyLongtime執(zhí)行了多長(zhǎng)時(shí)間,始終最后會(huì)直接返回$result這個(gè)數(shù)組的結(jié)果。這樣,你就可以通過(guò)mocking技術(shù)很輕易地去實(shí)現(xiàn)在單元測(cè)試中,繞過(guò)某些復(fù)雜的邏輯部分,而節(jié)省大量的寶貴時(shí)間提高測(cè)試效率。

下面的這個(gè)例子,講解的是Mocking技術(shù)中的更高級(jí)用法Mockbuilder。依然以上面的例子說(shuō)明:

 
 
 
  1. public function testReallyLongRunBuilder()  
  2. {  
  3. $stub = $this->getMockBuilder('Database')  
  4. ->setMethods(array(  
  5. 'reallyLongTime' 
  6. ))  
  7. ->disableAutoload()  
  8. ->disableOriginalConstructor()  
  9. ->getMock();  
  10. $result = array(array(1,'foo','bar test'));  
  11. $stub->expects($this->any())  
  12. ->method('reallyLongTime')  
  13. ->will($this->returnValue($result));  
  14. $this->assertGreaterThan(0,count($return));  
  15. }  
  16. ?> 

通過(guò)使用Mockbuilder,我們可以不用通過(guò)構(gòu)造函數(shù)的方法去初始化一個(gè)mock對(duì)象。這段代碼跟上一段代碼的功能其實(shí)是一樣的,只不過(guò)留意一下新的兩個(gè)方法: disableAutoload和disableOriginalConstructor,其功能分別是禁止使用PHP的內(nèi)置的autoload初始構(gòu)造方法和禁止調(diào)用該類原有的構(gòu)造函數(shù)。最后再看一個(gè)例子:

 
 
 
  1. /**  
  2. * Testing enforcing the type to "array" like the "enforceTypes"  
  3. * method does via type hinting  
  4. */ 
  5. public function ttestReallyLongRunBuilderConstraint()  
  6. {  
  7. $stub = $this->getMock('Database',array('reallyLongTime'));  
  8. $stub->expects($this->any())  
  9. ->method('reallyLongTime')  
  10. ->with($this->isType('array'));  
  11. $arr = array('test');  
  12. $this->assertTrue($stub-> reallyLongTime ($arr));  
  13. }  
  14. ?> 

在這里,我們使用了with方法,其中這個(gè)方法中指定了要傳入的參數(shù)類型為array數(shù)組類型,最后這個(gè)斷言是通過(guò)了,因?yàn)榉祷氐牡拇_是數(shù)組類型。


分享名稱:PHP單元測(cè)試?yán)鳎篜HPUnit深入理解
標(biāo)題鏈接:http://www.5511xx.com/article/dhocdei.html