|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
然后大吼:别人可以,我为什么就不可以?(是不是有点阎罗教练的味道,默默的确是电影看多了,抽嘴巴是会痛的,各位其实明白这个道理了就行了) 测试驱动的开辟和单位测试是确保代码在经由修正和严重调剂以后仍然能如咱们希冀的一样任务的最新办法。在本文中,您将进修到若何在模块、数据库和用户界面(UI)层对本人的 PHP 代码停止单位测试。
如今是清晨 3 点。咱们如何才干晓得本人的代码仍然在任务呢?
Web 使用法式是 24x7 不中断运转的,因而我的法式是不是还在运转这个成绩会在早晨一向困扰我。单位测试已帮我对本人的代码创立了足够的信念 ―― 如许我就能够平稳地睡个好觉了。
单位测试 是一个为代码编写测试用例并主动运转这些测试的框架。测试驱动的开辟 是一种单位测试办法,其思惟是应当起首编写测试法式,并验证这些测试可以发明毛病,然后才入手下手编写需求经由过程这些测试的代码。当一切测试都经由过程时,咱们开辟的特征也就完成了。这些单位测试的价值是咱们可以随时运转它们 ―― 在签入代码之前,严重修正以后,或安排到正在运转的体系以后都可以。
PHP 单位测试
关于 PHP 来讲,单位测试框架是 PHPUnit2。可使用 PEAR 号令行作为一个 PEAR 模块来装置这个体系:% pear install PHPUnit2。
在装置这个框架以后,可以经由过程创立派生于 PHPUnit2_Framework_TestCase 的测试类来编写单位测试。
模块单位测试
我发明入手下手单位测试最好的中央是在使用法式的营业逻辑模块中。我利用了一个复杂的例子:这是一个对两个数字停止乞降的函数。为了入手下手测试,咱们起首编写测试用例,以下所示。
清单 1. TestAdd.php
<?php
require_once 'Add.php';
require_once 'PHPUnit2/Framework/TestCase.php';
class TestAdd extends PHPUnit2_Framework_TestCase
{
function test1() { $this->assertTrue( add( 1, 2 ) == 3 ); }
function test2() { $this->assertTrue( add( 1, 1 ) == 2 ); }
}
?>
这个 TestAdd 类有两个办法,都利用了 test 前缀。每一个办法都界说了一个测试,这个测试可以与清单 1 一样复杂,也能够非常庞杂。在本例中,咱们在第一个测试中只是复杂地判定 1 加 2 等于 3,在第二个测试中是 1 加 1 等于 2。
PHPUnit2 体系界说了 assertTrue() 办法,它用来测试参数中包括的前提值是不是为真。然后,咱们又编写了 Add.php 模块,最后让它发生毛病的了局。
清单 2. Add.php
<?php
function add( $a, $b ) { return 0; }
?>
如今运转单位测试时,这两个测试城市掉败。
清单 3. 测试掉败
% phpunit TestAdd.php
PHPUnit 2.2.1 by Sebastian Bergmann.
FF
Time: 0.0031270980834961
There were 2 failures:
1) test1(TestAdd)
2) test2(TestAdd)
FAILURES!!!
Tests run: 2, Failures: 2, Errors: 0, Incomplete Tests: 0.
如今我晓得这两个测试都可以正常任务了。因而,可以修正 add() 函数来真正地做实践的工作了。
<?php
function add( $a, $b ) { return $a+$b; }
?>
如今这两个测试都可以经由过程了。
清单 4. 测试经由过程
% phpunit TestAdd.php
PHPUnit 2.2.1 by Sebastian Bergmann.
..
Time: 0.0023679733276367
OK (2 tests)
%
虽然这个测试驱动开辟的例子十分复杂,然而咱们可以从中体味到它的思惟。咱们起首创立了测试用例,而且有足够多的代码让这个测试运转起来,不外了局是毛病的。然后咱们验证测试切实其实是掉败的,接实在现了实践的代码使这个测试可以经由过程。
我发明在完成代码时我会一向不休地添加代码,直到具有一个掩盖一切代码途径的完全测试为止。在本文的最初,您会看到有关编写甚么测试和若何编写这些测试的一些建议。
数据库测试
在停止模块测试以后,就能够停止数据库会见测试了。数据库会见测试 带来了两个风趣的成绩。起首,咱们必需在每次测试之前将数据库恢复到某个已知点。其次,要注重这类恢复能够会对现无数据库形成损坏,因而咱们必需对非临盆数据库停止测试,或在编写测试用例时注重不克不及影响现无数据库的内容。
数据库的单位测试是从数据库入手下手的。为了论述这个成绩,咱们需求利用上面的复杂形式。
清单 5. Schema.sql
DROP TABLE IF EXISTS authors;
CREATE TABLE authors (
id MEDIUMINT NOT NULL AUTO_INCREMENT,
name TEXT NOT NULL,
PRIMARY KEY ( id )
);
清单 5 是一个 authors 表,每笔记录都有一个相干的 ID。
接上去,就能够编写测试用例了。
清单 6. TestAuthors.php
<?php
require_once 'dblib.php';
require_once 'PHPUnit2/Framework/TestCase.php';
class TestAuthors extends PHPUnit2_Framework_TestCase
{
function test_delete_all() {
$this->assertTrue( Authors::delete_all() );
}
function test_insert() {
$this->assertTrue( Authors::delete_all() );
$this->assertTrue( Authors::insert( 'Jack' ) );
}
function test_insert_and_get() {
$this->assertTrue( Authors::delete_all() );
$this->assertTrue( Authors::insert( 'Jack' ) );
$this->assertTrue( Authors::insert( 'Joe' ) );
$found = Authors::get_all();
$this->assertTrue( $found != null );
$this->assertTrue( count( $found ) == 2 );
}
}
?>
这组测试掩盖了从表中删除作者、向表中拔出作者和在验证作者是不是存在的同时拔出作者等功效。这是一个累加的测试,我发明关于寻觅毛病来讲这十分有效。察看一下哪些测试可以正常任务,而哪些测试不克不及正常任务,就能够疾速地找出哪些中央失足了,然后就能够进一步了解它们之间的区分。
最后发生掉败的 dblib.php PHP 数据库会见代码版本以下所示。
清单 7. dblib.php
<?php
require_once('DB.php');
class Authors
{
public static function get_db()
{
$dsn = 'mysql://root:password@localhost/unitdb';
$db =& DB::Connect( $dsn, array() );
if (PEAR::isError($db)) { die($db->getMessage()); }
return $db;
}
public static function delete_all()
{
return false;
}
public static function insert( $name )
{
return false;
}
public static function get_all()
{
return null;
}
}
?>
对清单 8 中的代码履行单位测试会显示这 3 个测试全体掉败了:
清单 8. dblib.php
% phpunit TestAuthors.php
PHPUnit 2.2.1 by Sebastian Bergmann.
FFF
Time: 0.007500171661377
There were 3 failures:
1) test_delete_all(TestAuthors)
2) test_insert(TestAuthors)
3) test_insert_and_get(TestAuthors)
FAILURES!!!
Tests run: 3, Failures: 3, Errors: 0, Incomplete Tests: 0.
%
如今咱们可以入手下手添加准确会见数据库的代码 ―― 一个办法一个办法地添加 ―― 直到一切这 3 个测试都可以经由过程。终究版本的 dblib.php 代码以下所示。
清单 9. 完全的 dblib.php
<?php
require_once('DB.php');
class Authors
{
public static function get_db()
{
$dsn = 'mysql://root:password@localhost/unitdb';
$db =& DB::Connect( $dsn, array() );
if (PEAR::isError($db)) { die($db->getMessage()); }
return $db;
}
public static function delete_all()
{
$db = Authors::get_db();
$sth = $db->prepare( 'DELETE FROM authors' );
$db->execute( $sth );
return true;
}
public static function insert( $name )
{
$db = Authors::get_db();
$sth = $db->prepare( 'INSERT INTO authors VALUES (null,?)' );
$db->execute( $sth, array( $name ) );
return true;
}
public static function get_all()
{
$db = Authors::get_db();
$res = $db->query( "SELECT * FROM authors" );
$rows = array();
while( $res->fetchInto( $row ) ) { $rows []= $row; }
return $rows;
}
}
?>
在对这段代码运转测试时,一切的测试都可以没有成绩地运转,如许咱们就能够晓得本人的代码可以准确任务了。
HTML 测试
对全部 PHP 使用法式停止测试的下一个步调是对前真个超文本标志言语(HTML)界面停止测试。要停止这类测试,咱们需求一个以下所示的 Web 页面。
这个页面临两个数字停止乞降。为了对这个页面停止测试,咱们起首从单位测试代码入手下手动手。
清单 10. TestPage.php
<?php
require_once 'HTTP/Client.php';
require_once 'PHPUnit2/Framework/TestCase.php';
class TestPage extends PHPUnit2_Framework_TestCase
{
function get_page( $url )
{
$client = new HTTP_Client();
$client->get( $url );
$resp = $client->currentResponse();
return $resp['body'];
}
function test_get()
{
$page = TestPage::get_page( 'http://localhost/unit/add.php' );
$this->assertTrue( strlen( $page ) > 0 );
$this->assertTrue( preg_match( '/<html>/', $page ) == 1 );
}
function test_add()
{
$page = TestPage::get_page( 'http://localhost/unit/add.php?a=10&b=20' );
$this->assertTrue( strlen( $page ) > 0 );
$this->assertTrue( preg_match( '/<html>/', $page ) == 1 );
preg_match( '/<span id="result">(.*?)<\/span>/', $page, $out );
$this->assertTrue( $out[1]=='30' );
}
}
?>
这个测试利用了 PEAR 供应的 HTTP Client 模块。我发明它比内嵌的 PHP Client URL Library(CURL)更复杂一点儿,不外也能够利用后者。
有一个测试会反省所前往的页面,并判别这个页面是不是包括 HTML。第二个测试会经由过程将值放到恳求的 URL 中来恳求盘算 10 和 20 的和,然后反省前往的页面中的了局。
这个页面的代码以下所示。
清单 11. TestPage.php
<html><body><form>
<input type="text" name="a" value="<?php echo($_REQUEST['a']); ?>" /> +
<input type="text" name="b" value="<?php echo($_REQUEST['b']); ?>" /> =
<span id="result"><?php echo($_REQUEST['a']+$_REQUEST['b']); ?></span>
<br/>
<input type="submit" value="Add" />
</form></body></html>
这个页面相当复杂。两个输出域显示了恳求中供应确当前值。了局 span 显示了这两个值的和。<span> 标志标出了一切区分:它关于用户来讲是不成见的,然而关于单位测试来讲倒是可见的。因而单位测试其实不需求庞杂的逻辑来找到这个值。相反,它会检索一个特定 <span> 标志的值。如许当界面产生变更时,只需 span 存在,测试就能够经由过程。
与后面一样,起首编写测试用例,然后创立一个掉败版本的页面。咱们对掉败情形停止测试,然后修正页面的内容使其可以任务。了局以下:
清单 12. 测试掉败情形,然后修正页面
% phpunit TestPage.php
PHPUnit 2.2.1 by Sebastian Bergmann.
..
Time: 0.25711488723755
OK (2 tests)
%
这两个测试都可以经由过程,这就意味着测试代码可以正常任务。
不外对 HTML 前真个测试有一个缺点:JavaScript。超文本传输协定(HTTP)客户机代码对页面停止检索,然而却没有履行 JavaScript。因而假如咱们在 JavaScript 中有良多代码,就必需创立用户代办署理级的单位测试。我发明完成这类功效的最好办法是利用 Microsoft |
|