PHPUnit + Xdebug 单元测试技术
Click here to load reader
description
Transcript of PHPUnit + Xdebug 单元测试技术
PHPUnit + Xdebug 单元测试技术
—— 提升编码质量的工具和方法
讨论的主题• PHPUnit 单元测试技术• Xdebug 调试
声明• 这仅是一个宣传、引导• 会写测试并提升了 工作质量、减少返工次
数 的程序员,牧师 老大说了,会更容易得到管理层的奖励和青睐
如何测试…测试基础
一个例子 : Stack.php<?phpclass Stack{ private $store;
public function __construct() { $this->store = array(); }
public function push($o) { array_push($this->store, $o); }
public function pop() { return array_pop($this->store); }
public function count() { return count($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 来帮忙<?php
require_once './Stack.php';
class StackTest extends 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());
}
}
执行测试$ phpunit StackTest
执行测试成功时 :
执行测试失败时 :
- 互动演示 -
Stack 增加新功能时…<?phpclass Stack{ private $store;
public function __construct() { $this->store = array(); }
public function push($o) { array_push($this->store, $o); }
public function pop() { return array_pop($this->store); }
public function count() { return count($this->store); }
public function clean() { $this->store = array(); }}
<?php
require_once './Stack.php';
class StackTest extends 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 示例<?php
class IndexControllerTest extends Zend_Test_PHPUnit_ControllerTestCase
{
…
public function testIndexAction()
{
$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 示例$ symfony generate:project demo
$ symfony generate:app foo
$ symfony generate:module foo bar
$ symfony test:all
$ symfony test:functional foo
Symfony 示例<?php
include(dirname(__FILE__).'/../../bootstrap/functional.php');
$browser = new sfTestFunctional(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 cov StackTest
代码覆盖报告,一种调试工具。
- 互动演示 -
生成规格文档$ phpunit --testdox StackTest
测试 即是 定义。
生成规格文档
- 互动演示 -
测试失败时…Xdebug 来帮忙
Xdebug 出场…• 简便的 PHP 调试、跟踪 和 剖析 工具
–我们主要用它的 远程调试功能
如何调试一个测试 ?$ export XDEBUG_CONFIG="remote_host=192.168.8.91 idekey=netbeans-xdebug"
## plain old php
$ phpunit StackTest
## zend framework
$ phpunit application/controllers/FooControllerTest
## symfony 1
$ symfony test:functional foo
断点
单步
上下文
- 互动演示 -
Xdebug 的其它能力…• 跟踪 (Trace)• 性能剖析 (Profile)
一点疑惑
断言… 可以吗 ?
• 这种定义事物的方式可能并不充分–但至少是必要的
• 可是仍然是不够充分…–世界上从来没有 ‘完全充分’,就像合同不够充分,法律不会完美一样
–我们的目标是测试案例能覆盖到所有的代码路径即可
测试不是测试组的事吗 ?
• 单元测试 习惯上、实践上 是 程序员写的– 只有程序员自己知道怎么测…–而且,它也是一种调试工具
• 功能测试 习惯上 是 测试组 来做–但仍然需要程序员的紧密配合
最后…
…谢谢 !!