測試驅動開發網上也談了不少了,PHP方面的文章也有一些,在百度和Google裏搜,好像沒有看到幾篇談用Mock(假裝對象)的技術的,這裏寫篇文章講講。php
先過一下測試驅動開發的基本理念:就是先寫測試用例(通常這個測試用例都是自動化的單元測試用例,便於快速回滾執行),而後經過逐步修復測試用例的方法補齊產品代碼,最後測試用例修復完畢後,產品也就寫完了。ubuntu
從我本身的實踐中,我認爲在類庫開發的時候使用測試驅動開發技術是一個很好的方案,理由以下:
可以寫出測試用例,即說明對問題域已經有一個清晰的瞭解,
節省了寫文檔的時間,測試用例就是類庫調用的示例代碼了。
代碼質量有保證,由於寫類庫的過程就是修復測試用例的過程,因此測試用例修復完畢後類庫也就寫完了。
便於估時,估計類庫開發時間的問題就簡化成估計修復測試用例的時間了,相對於來講估時容易一些。svn
咱們以編寫一個字符串轉數字的函數爲例講解測試驅動開發的理念,再引入Mock技術。在開始以前,須要安裝PHPUnit和Mockery庫(本文不使用PHPUnit自帶的Mock庫):函數
# 安裝PHPUnitpear config-set auto_discover 1 pear install pear.phpunit.de/PHPUnit# 安裝Mock庫sudo pear channel-discover pear.survivethedeepend.com sudo pear channel-discover hamcrest.googlecode.com/svn/pear sudo pear install --alldeps deepend/Mockery
那從測試驅動開發的理念來作的話,咱們先寫測試用例 – parseinttest.php:單元測試
<? class ParseIntTest extends PHPUnit_Framework_TestCase { public function testParseIntBasic() { $v = parse_int("12345"); $this->assertEquals(12345, $v); $v = parse_int("-12345"); $this->assertEquals(-12345, $v); /* $v = parse_int("abcd"); $this->assertEquals(0, $v); $v = parse_int("0xab12"); $this->assertEquals(0xab12, $v); $v = parse_int("01b"); $this->assertEquals(1, $v); */ } } ?>
上面的代碼裏,咱們經過單元測試用例指定了要實現的函數parse_int的需求,便可以解析整數、帶符號的整數等,又由於時間和資源的限制,那咱們去掉了對十六進制和二進制的支持。測試
運行下面的命令執行測試用例:this
phpunit --verbose parseinttest
由於這個時間沒有任何代碼,因此獲得指望的錯誤 – PHPUnit告訴咱們找不到parse_int這個函數:
PHPUnit 3.6.12 by Sebastian Bergmann.google
PHP Fatal error: Call to undefined function parse_int() in /var/www/pmdemo/parseinttest2.php on line 6spa
那咱們先建一個空的parse_int函數 – parseint.php:.net
<? function parse_int($str) { return 0; } ?>
上面的代碼裏咱們先不實現函數parse_int的任何邏輯,再運行測試用例,獲得:
shiyimin@ubuntu :/var/www/pmdemo$ phpunit --verbose parseinttest
PHPUnit 3.6.12 by Sebastian Bergmann.
F
Time: 0 seconds, Memory: 2.75Mb
There was 1 failure:
1) ParseIntTest::testParseIntBasic
Failed asserting that 0 matches expected 12345.
/var/www/pmdemo/parseinttest.php:7
FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
從上面紅色高亮顯示出來的錯誤消息,咱們知道是測試用例裏的第一個判斷沒有經過,即下面的判斷語句沒有執行成功:
$this->assertEquals(12345, $v);
這是由於咱們的parse_int函數老是返回0這個值,發現了這個錯誤,咱們來補齊這個邏輯以修復測試用例:
parseint.php
<? function parse_int($str) { $result = 0; $i = 0; for ( $i = 0; $i < strlen($str); ++$i ) { $result *= 10; $result += $str[$i] - '0'; } return $result; } ?>
再次運行測試用例:
shiyimin@ubuntu :/var/www/pmdemo$ phpunit --verbose parseinttest
PHPUnit 3.6.12 by Sebastian Bergmann.
F
Time: 0 seconds, Memory: 2.75Mb
There was 1 failure:
1) ParseIntTest::testParseIntBasic
Failed asserting that 12345 matches expected -12345.
/var/www/pmdemo/parseinttest.php:10
FAILURES!
Tests: 1, Assertions: 2, Failures: 1.
從上面的結果能夠看出,第一個測試用例已經修復了,如今是在處理帶符號的字符串時,發生了問題,繼續修復代碼:
parseint.php
<? function parse_int($str) { $result = 0; $i = 0; $neg = 1; if ( $str[0] == '-' ) { $neg = -1; } for ( $i = 0; $i < strlen($str); ++$i ) { $result *= 10; $result += $str[$i] - '0'; } return $result * $neg; } ?>
再運行測試用例:
shiyimin@ubuntu :/var/www/pmdemo$ phpunit --verbose parseinttest
PHPUnit 3.6.12 by Sebastian Bergmann.
.
Time: 0 seconds, Memory: 2.75Mb
OK (1 test, 2 assertions)
好了,此次全部測試用例都經過了,那咱們的產品代碼的實現也告一段落了,固然文章爲了講解方便,上面的代碼並非一個完整的實現,例如它就沒法處理字符串「+12345」的情形。
下篇文章講解如何在PHPUnit裏應用Mock技術。