天天看點

php單元測試

     你是否在程式開發的過程中遇到下面的情況:當你花了非常長的時間開發一個應用後,你覺得應該是大功告成了,可惜在調試的時候,老是不斷的發現bug,并且最可怕的是,這些bug是反複出現的,你可能發現這些bug之間會有關聯,但卻老是找不到問題的所在。

  當你遇到以上這些令你沮喪的情況時,你一定會想能有什麼更好的辦法去解決呢?辦法當然是有的!這就是使用單元測試。單元測試不但能夠在一定程度上解決上述頭疼的問題,并且能讓代碼變的easy維護,還能夠能讓你很多其它地對代碼進行重構。

  一旦你編寫好單元測試用例,當你須要改動你的代碼時,你要做的事情就是又一次執行你的單元測試用例并觀察這些單元測試用例是否能通過,假設通過了的話,證明代碼是沒問題的。

  人們往往會說:既然單元測試這麼好,為什麼那麼多人還是不大願意去寫單元測試呢?有下面幾種了解上的誤曲:

  1、覺得編寫單元測試太浪費時間。盡管眼下非常多IDE工具都為編寫單元測試建立好了架構,但還是要開發人員編寫一些單元測試的代碼的。就象非常多開發中的最佳實踐一樣,用正确的方法去做正确的事情會為開發節省大量的時間。每當新添加新功能時,你可能通過訪問你的網頁到處去點選手動測試,而執行建立好的單元測試用例其速度事實上比通過手工去測試的速度更快。

  2、覺得既然代碼能執行了,不須要再編寫單元測試。但假設團隊中有新的成員,假設沒有良好的單元測試用例,新成員非常有可能任意地去編碼而不考慮各種後果。假設有編寫良好的單元測試,在程式執行時進行各種測試,則能最大程度避免bug的産生。

  3、覺得編寫單元測試代碼枯燥無味。程式猿的天性是解決這個問題,而非常多程式猿覺得在緊張的編碼工作時,還要編寫單元測試代碼,會非常枯燥。但要知道的是,假設能通過編寫單元測試在非常早的階段就能盡可能發現代碼中多的錯誤的話,那麼既節省時間降低了出錯,何樂而不為?

  開始動手安裝phpunit

  本文中将通過介紹php中的單元測試利器phpunit(http://phpunit.de/),并通過實際樣例來解說怎樣在實際工作中運用phpunit。首先安裝phpunit的方法能夠通過php下的pear去安裝:

pear channel-discover pear.phpunit.de
  pear channel-discover components.ez.no
  pear channel-discover pear.symfony-project.com
    pear install phpunit/PHPUnit      

假設你想通過手動方式去安裝,能夠參考phpunit的手冊去安裝(http://www.phpunit.de/manual/3.0/en/installation.html)。

  編寫第一個單元測試用例

  以下我們開始編寫第一個單元測試用例。在編寫測試用例時,要遵守例如以下的phpunit的規則:

  1 一般地,在測試用例中,能夠擴充PHPUnit_Framework_TestCase類,這樣就能夠使用象setUp(),tearDown()等方法了。

  2 測試用例的名字最好是使用約定俗成的格式,即在被測試類的後面加上”Test”,比方要測試的類為RemoteConnect,則測試用例的命名為RemoteConnectTest。

  3 在一個測試用例中的全部的測試方法,在命名時都應該以test+測試方法名去命名,如testDoesLikeWaffles(),要注意的是該方法必須是聲明為public類型的。當然能夠在你的測試用例中包括private的方法,但它們不能被phpunit所調用。

  4 測試方法中是不能接收參數的。

  以下首先舉個簡單的樣例,代碼例如以下:

<?php
class RemoteConnect
{
  public function connectToServer($serverName=null)
  {
    if($serverName==null){
      throw new Exception(“That's not a server name!”);
    }
    $fp = fsockopen($serverName,80);
    return ($fp) ? true : false;
  }
  public function returnSampleObject()
  {
    return $this;
  }
}
?>      

上面的代碼事實上是實作連接配接到一個指定的伺服器的功能,那麼我們能夠編寫測試代碼例如以下:

<?php
require_once('RemoteConnect.php');
class RemoteConnectTest extends PHPUnit_Framework_TestCase
{
  public function setUp(){ }
  public function tearDown(){ }
  public function testConnectionIsValid()
  {
   // test to ensure that the object from an fsockopen is valid
   $connObj = new RemoteConnect();
    $serverName = 'www.google.com';
    $this->assertTrue($connObj->connectToServer($serverName) !== false);
 }
}
?>      

 在上面的代碼中,因為繼承了PHPUnit_Framework_TestCase類,是以在setUp和tearDown方法中,不須要編寫不論什麼代碼。SetUp方法是在每一個測試用例執行前進行一些初始化的工作,而tearDown則在每一個測試用例執行後進行一些比方資源的釋放等工作。在測試方法中,通過使用phpunit的斷言assertTrue去推斷所傳回的布爾值是否為真,這裡是通過調用RemoteConnect.php中的connectToServe方法去推斷是否能連接配接上server。

接下來我們執行這個單元測試,在指令行下輸入代碼:

  phpunit /path/to/tests/RemoteConnectTest.php就可以,能夠看到測試順利通過的話,會輸出下面結果:

PHPUnit 3.4 by Sebastian Bergmann.
Time: 1 second
Tests: 1, Assertions: 1, Failures 0      

能夠看到,上面是通過了測試。預設情況下,phpunit是會執行測試用例中的全部測試方法的。以下再介紹下phpunit中相關的幾個斷言:

AssertTrue/AssertFalse    斷言是否為真值還是假
AssertEquals    推斷輸出是否和預期的相等
AssertGreaterThan    斷言結果是否大于某個值,相同的也有LessThan(小于),GreaterThanOrEqual(大于等于),
LessThanOrEqual(小于等于).
AssertContains    推斷輸入是否包括指定的值
AssertType    推斷是否屬于指定類型
AssertNull    推斷是否為空值
AssertFileExists    推斷檔案是否存在
AssertRegExp    依據正則表達式推斷      

舉個樣例來說明下比方AssertType的使用,依舊以上面的樣例來說,能夠用AssertType去推斷returnSampleObject傳回的對象執行個體是否為remoteConnect,代碼例如以下:

<?php
function testIsRightObject() {
  $connObj = new RemoteConnect();
  $returnedObject = $connObj->returnSampleObject();
  $this->assertType('remoteConnect', $returnedObject);
}
?>      

 眼下PHP架構對單元測試的支援

  眼下非常多優秀的php架構(如Zend Framework,Symfony等),都提供了對單元測試非常好的支援。以Zend Framework為例,說明下當中是怎樣執行單元測試的。

<?php
class CommentControllerTest extends Zend_Test_PHPUnit_ControllerTestCase
{
  public function setUp()
  {
    parent::setUp();
  }
  public function tearDown()
  {
    parent::tearDown();
  }
  public function appBootstrap()