在編寫(xiě)PHPUnit單元測試代碼時(shí),其實(shí)很多都是對各個(gè)類(lèi)的各個(gè)外部調用的函數進(jìn)行測試驗證,檢測代碼覆蓋率,驗證預期效果。為避免增加開(kāi)發(fā)量,可以使用PHPUnit提供的phpunit-skelgen來(lái)生成測試骨架。只是一開(kāi)始我不知道有這個(gè)腳本,就自己寫(xiě)了一個(gè),大大地提高了開(kāi)發(fā)效率,也不用為另外投入時(shí)間去編寫(xiě)測試代碼而煩心。并且發(fā)現自定義的腳本比phpunit-skelgen更具人性化。所以在這里分享一下。
假如我們現在有一個(gè)簡(jiǎn)單的業(yè)務(wù)類(lèi),實(shí)現了加運算,為了驗證其功能,下面將會(huì )就兩種生成測試代碼的方式進(jìn)行說(shuō)明。
在安裝了phpunit-skelgen后,可以使用以下命令來(lái)生成測試骨架。
生成后,使用:
可查看到生成的測試代碼如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | <?php/** * Generated by PHPUnit_SkeletonGenerator 1.2.1 on 2014-06-30 at 15:53:01. */class DemoTest extends PHPUnit_Framework_TestCase{ /** * @var Demo */ protected $object; /** * Sets up the fixture, for example, opens a network connection. * This method is called before a test is executed. */ protected function setUp() { $this->object = new Demo; } /** * Tears down the fixture, for example, closes a network connection. * This method is called after a test is executed. */ protected function tearDown() { } /** * @covers Demo::inc * @todo Implement testInc(). */ public function testInc() { // Remove the following lines when you implement this test. $this->markTestIncomplete( 'This test has not been implemented yet.' ); }} |
試運行測試一下:
可以看到?jīng)]有將需要的測試類(lèi)包括進(jìn)來(lái)。當然還有其他一些需要手工改動(dòng)的地方。
現在改用自定義的腳本 來(lái)生成,雖然也有需要手工改動(dòng)的地方,但已經(jīng)盡量將需要改動(dòng)的代碼最小化,讓測試人員(很可能是開(kāi)發(fā)人員自己)更關(guān)注業(yè)務(wù)的測試。
先看一下Usage.
然后可以使用:
來(lái)預覽看一下將要生成的測試代碼,如果沒(méi)有問(wèn)題可以使用:
將生成的測試代碼保存起來(lái)。注意:這里使用的是“_Test.php”后綴,以便和官方的區分??聪律傻拇a:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | <?php/** * PhpUnderControl_AppAds_Test * * 針對 ./Demo.php Demo 類(lèi)的PHPUnit單元測試 * * @author: dogstar 20140630 */if (!class_exists('Demo')) { require dirname(__FILE__) . '/' . './Demo.php';}class PhpUnderControl_Demo_Test extends PHPUnit_Framework_TestCase{ public $demo; protected function setUp() { parent::setUp(); $this->demo = new Demo(); } protected function tearDown() { } /** * @group returnFormat */ public function testIncReturnFormat() { $left = ''; $right = ''; $rs = $this->demo->inc($left, $right); } /** * @depends testIncReturnFormat * @group businessData */ public function testIncBusinessData() { $left = ''; $right = ''; $rs = $this->demo->inc($left, $right); }} |
隨后,試運行一下:
測試通過(guò)了?。?!
起碼,我覺(jué)得生成的代碼在大多數默認情況下是正常通過(guò)的話(huà),可以給開(kāi)發(fā)人員帶上心理上的喜悅,從而很容易接受并樂(lè )意去進(jìn)行下一步的測試用例完善。
現在,開(kāi)發(fā)人員只須稍微改動(dòng)測試代碼就可以實(shí)現對業(yè)務(wù)的驗證。如下示例:
然后再運行,依然通過(guò)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | <?php/** * 單元測試骨架代碼自動(dòng)生成腳本 * 主要是針對當前項目系列生成相應的單元測試代碼,提高開(kāi)發(fā)效率 * * 用法: * Usage: php ./build_phpunit_test_tpl.php <file_path> <class_name> [bootstrap] [author = dogstar] * * 1、針對全部public的函數進(jìn)行單元測試 * 2、各個(gè)函數對應返回格式測試與業(yè)務(wù)數據測試 * 3、源文件加載(在沒(méi)有自動(dòng)加載的情況下) * * 備注:另可使用phpunit-skelgen進(jìn)行骨架代碼生成 * * @author: dogstar 20140630 * @version: 2.0.0 */if ($argc < 3) { die("Usage: php $argv[0] <file_path> <class_name> [bootstrap] [author = dogstar]\n");}$filePath = $argv[1];$className = $argv[2];$bootstrap = isset($argv[3]) ? $argv[3] : null;$author = isset($argv[4]) ? $argv[4] : 'dogstar';if (!empty($bootstrap)) { require $bootstrap;}require $filePath;if (!class_exists($className)) { die("Error: cannot find class($className). \n");}$reflector = new ReflectionClass($className);$methods = $reflector->getMethods(ReflectionMethod::IS_PUBLIC);date_default_timezone_set('Asia/Shanghai');$objName = lcfirst(str_replace('_', '', $className));$code = "<?php/** * PhpUnderControl_AppAds_Test * * 針對 $filePath $className 類(lèi)的PHPUnit單元測試 * * @author: $author " . date('Ymd') . " */";if (file_exists(dirname(__FILE__) . '/test_env.php')) { $code .= "require_once dirname(__FILE__) . '/test_env.php';";}$code .= "if (!class_exists('$className')) { require dirname(__FILE__) . '/' . '$filePath';}class PhpUnderControl_" . str_replace('_', '', $className) . "_Test extends PHPUnit_Framework_TestCase{ public \$$objName; protected function setUp() { parent::setUp(); \$this->$objName = new $className(); } protected function tearDown() { }";foreach ($methods as $method) { if($method->class != $className) continue; $fun = $method->name; $Fun = ucfirst($fun); if (strlen($Fun) > 2 && substr($Fun, 0, 2) == '__') continue; $rMethod = new ReflectionMethod($className, $method->name); $params = $rMethod->getParameters(); $isStatic = $rMethod->isStatic(); $isConstructor = $rMethod->isConstructor(); if($isConstructor) continue; $initParamStr = ''; $callParamStr = ''; foreach ($params as $param) { $initParamStr .= " \$" . $param->name . " = '';"; $callParamStr .= '$' . $param->name . ', '; } $callParamStr = empty($callParamStr) ? $callParamStr : substr($callParamStr, 0, -2); $code .= " /** * @group returnFormat */ public function test$Fun" . "ReturnFormat() {" . (empty($initParamStr) ? '' : "$initParamStr\n") . ' ' . ($isStatic "\$rs = $className::$fun($callParamStr);": "\$rs = \$this->$objName->$fun($callParamStr);") . " }"; $code .= " /** * @depends test$Fun" . "ReturnFormat * @group businessData */ public function test$Fun" . "BusinessData() {" . (empty($initParamStr) ? '' : "$initParamStr\n") . ' ' . ($isStatic "\$rs = $className::$fun($callParamStr);": "\$rs = \$this->$objName->$fun($callParamStr);") . " }";}$code .= "}";echo $code;echo "\n"; |
聯(lián)系客服