PHPUnit + Xdebug单元测试技术——提升编码质量的工具和方法
讨论的主题PHPUnit单元测试技术Xdebug调试
声明这仅是一个宣传、引导会写测试并提升了 工作质量、减少返工次数 的程序员,牧师 老大说了,会更容易得到管理层的奖励和青睐
如何测试…测试基础
一个例子: Stack.php<?phpclassStack{private $store;publicfunction__construct() {                  $this->store = array();        }publicfunctionpush($o) {array_push($this->store, $o);        }publicfunctionpop() {returnarray_pop($this->store);        }publicfunctioncount() {returncount($this->store);        }}
如何保证正确性?手眼测试…在程序底部添加一些测试语句$stack = new Stack;if ($stack) {echo $stack->count();    $stack->push(1111);echo $stack->count();echo $stack->pop();echo $stack->count();}
测试成功后…剔除/注释 掉这些语句…
下一次再修改时…重新加上这些测试语句, 并再次测试回归测试
问题是…每次修改都要重新加上这些测试, 以验证没有(未预期地) 破坏原有功能…好吧, 为何不把测试独立出来呢?
好主意!!把测试独立成一个文件, 每次执行一下即可无需再做 剔除/注释 – 加上/反注释 工作了StackTest.php-------------------------------------------------------------------------------------------<?phprequire_once ‘./Stack.php’;$stack = new Stack;if ($stack) {echo $stack->count();    $stack->push(1111);echo $stack->count();echo $stack->pop();echo $stack->count();}
新问题是…每次都要 手眼测试, 太累了!!!呣, 有没有办法让 机器 去做?然后自己就有时间去 把妹子…
PHPUnit来帮忙<?phprequire_once'./Stack.php';class StackTestextends PHPUnit_Framework_TestCase{public function testCanPushAndPop() {        $stack = new Stack();        $this->assertEquals(0, $stack->count());        $stack->push('foo');        $this->assertEquals(1, $stack->count());        $this->assertEquals('foo', $stack->pop());        $this->assertEquals(0, $stack->count());    }}
执行测试$ phpunitStackTest
执行测试成功时:
执行测试失败时:
- 互动演示 -
Stack 增加新功能时…<?phpclassStack{private $store;publicfunction__construct() {                  $this->store = array();        }publicfunctionpush($o) {array_push($this->store, $o);        }publicfunctionpop() {returnarray_pop($this->store);        }publicfunctioncount() {returncount($this->store);        }public function clean() {                 $this->store = array();        }}
<?phprequire_once'./Stack.php';class StackTestextends PHPUnit_Framework_TestCase{public function testCanPushAndPop() {        $stack = new Stack();        $this->assertEquals(0, $stack->count());        $stack->push('foo');        $this->assertEquals(1, $stack->count());        $this->assertEquals('foo', $stack->pop());        $this->assertEquals(0, $stack->count());    }    public function testCanClean() {        $stack = new Stack();        $stack->push('foo');        $this->assertEquals(1, $stack->count());        $stack->clean();        $this->assertEquals(0, $stack->count());    }}也要增加新测试…
- 互动演示 -
xUnit方法的优势减少验证 ‘代码正确性’ 的工作量自动化回归测试避免系统退步写测试用例可以促使程序员发现边界条件测试用例本身就是很好的示范代码和文档写测试用例可以促使程序员真正了解需求完备的测试可以给程序员以信心More…
什么是测试?测试是一组输入、执行条件以及预期结果的集合一组断言测试是一个产品的详细接口规格如何调用,结果值会是如何,etc.测试定义产品的每一个面测试是…这一页 PPT 本身就是个测试…
鸭子“When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck.”James Whitcomb Riley (1849 – 1916)(Duck typing)
人“人就是以为自己不是机器的机器”Wang Wenlin (1981 – ∞)以为自己不是机器的机器, 是人…
框架中的测试Zend Framework & Symfony 1
良好的测试支持Zend Framework 集成 PHPUnit支持Symfony 1 使用自己的 Lime 测试框架Symfony 2 已改用 PHPUnit …它们都能自动生成 功能测试 骨架…
Zend Framework 示例$ zf create project demo$ zf create controller foo$ phpunit
Zend Framework 示例<?phpclassIndexControllerTestextendsZend_Test_PHPUnit_ControllerTestCase{    …publicfunctiontestIndexAction()    {        $params = array('action' => 'index', 'controller' => 'Index', 'module' => 'default');        $urlParams = $this->urlizeOptions($params);        $url = $this->url($urlParams);        $this->dispatch($url);        // assertions        $this->assertModule($urlParams['module']);        $this->assertController($urlParams['controller']);        $this->assertAction($urlParams['action']);        $this->assertQueryContentContains("div#welcome h3", "This is your project's main page");    }}
- 互动演示 -
Symfony示例$ symfonygenerate:project demo$ symfonygenerate:appfoo$ symfonygenerate:modulefoo bar$ symfonytest:all$ symfonytest:functionalfoo
Symfony示例<?phpinclude(dirname(__FILE__).'/../../bootstrap/functional.php');$browser = newsfTestFunctional(new sfBrowser());$browser->get('/content/index')->with('request')->begin()->isParameter('module', 'content')->isParameter('action', 'index')->end()->with('response')->begin()->isStatusCode(200)->checkElement('body', '!/This is a temporary page/')->end();
- 互动演示 -
好消息…功能测试脚本, 测试组会帮忙制作…程序员只需做单元测试
PHPUnit还能做什么…
代码覆盖报告$ phpunit --coverage-html covStackTest代码覆盖报告,一种调试工具。
- 互动演示 -
生成规格文档$ phpunit --testdoxStackTest测试 即是 定义。
生成规格文档
- 互动演示 -
测试失败时…Xdebug来帮忙
Xdebug出场…简便的 PHP 调试、跟踪 和 剖析 工具我们主要用它的 远程调试功能
如何调试一个测试?$ export XDEBUG_CONFIG="remote_host=192.168.8.91idekey=netbeans-xdebug"## plain old php$ phpunitStackTest## zend framework$ phpunit application/controllers/FooControllerTest## symfony 1$ symfonytest:functionalfoo
断点单步上下文
- 互动演示 -
Xdebug的其它能力…跟踪 (Trace)性能剖析 (Profile)
一点疑惑
断言… 可以吗?这种定义事物的方式可能并不充分但至少是必要的可是仍然是不够充分…世界上从来没有 ‘完全充分’,就像合同不够充分,法律不会完美一样我们的目标是测试案例能覆盖到所有的代码路径即可
测试不是测试组的事吗?单元测试 习惯上、实践上 是 程序员写的只有程序员自己知道怎么测…而且,它也是一种调试工具功能测试 习惯上 是 测试组 来做但仍然需要程序员的紧密配合
最后…
…谢谢!!

PHPUnit + Xdebug 单元测试技术