快捷搜索:  as  test  1111  test aNd 8=8  test++aNd+8=8  as++aNd+8=8  as aNd 8=8

辉煌煌国际hui137登陆l:利用单元测试在每个层上对PHP代码进行检查



测试驱动的开拓和单元测试是确保代码在颠末改动和重大年夜调剂之后依然能如我们期望的一样事情的最新措施。在本文中,您将进修到若何在模块、数据库和用户界面(UI)层对自己的 PHP 代码进行单元测试。

现在是早晨 3 点。我们如何才能知道自己的代码依然在事情呢?

Web 利用法度榜样是 24x7 不间断运行的辉煌煌国际hui137登陆l,是以我的法度榜样是否还在运行这个问题会在晚上不停困扰我。单元测试已经帮我对自己的代码建立了足够的信心 —— 这样我就可以安稳地睡个好觉了。

单元测试 是一个为代码编写测试用例并自动运行这些测试的框架。测试驱动的开拓 是一种单元测试措施,其思惟是应该首先编写测试法度榜样,并验证这些测试可以发明差错,然后才开始编写必要经由过程这些测试的代码。当所有测试都经由过程期,我们开拓的特点也就完成了。这些单元测试的代价是我们可以随时运行它们 —— 在签入代码之前,重大年夜改动之后,或者支配到正在运行的系统之后都可以。

PHP 单元测试

对付 PHP 来说,单元测试框架是 PHPUnit2。可以应用 PEAR 敕令行作为一个 PEAR 模块来安装这个系统:% pear install PHPUnit2。

在安装这个框架之后,可以经由过程创建派生于 PHPUnit2_Framework_TestCase 的测试类来编写单元测试。

模块单元测试

我发明开始单元测试最好的地方是在利用法度榜样的营业逻辑模块中。我应用了一个简单的例子:这是一个对两个数字进行乞降的函数。为了开始测试,我们首先编写测试用例,如下所示。

清单 1. TestAdd.php

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

现在运行单元测试时,这两个测试都邑掉败。

清单 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() 函数来真正地做实际的工作了。

现在这两个测试都可以经由过程了。

清单辉煌煌国际hui137登陆l 4. 测试经由过程

% phpunit TestAdd.php

PHPUnit 2.2.1 by Sebastian Bergmann.

..

Time: 0.0023679733276367

OK (2 tests)

%

只管这个测试驱动开拓的例子异常简单,然则我们可以从中体会到它的思惟。我们起开创建了测试用例,并且有足够多的代码让这个测试运行起来,不过结果是差错的。然后我们验证测试切实着实是掉败的,接实在现了实际的代码使这个测试能够经由过程。

我发明在实今世码时我会不停赓续地添加代码,直到拥有一个覆盖所有代码路径的完备测试为止。在本文的着末,您会看到有关编写什么测试和若何编写这些测试的一些建议。

数据库测试

在进行模块测试之后,就可以进行数据库造访测试了。数据库造访测试 带来了两个有趣的问题。首先,我们必须在每次测试之前将数据库规复到某个已知点。其次,要留意这种规辉煌煌国际hui137登陆l复可能会对现稀有据库造成破坏,是以我们必须对非临盆数据库进行测试,或者在编写测试用例时留意不能影响现稀有据库的内容。

最初孕育发生掉败的 dblib.php PHP 数据库造访代码版本如下所示。

清单 7. dblib.php

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

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;

}

}

?>

这个测试应用了 PEAR 供给的 HTTP Client 模块。我发明它比内嵌的 PHP Client URL Library(CURL)更简单一点儿,不过也可以应用后者。

有一个测试会反省所返回的页面,并判断这个页面是否包孕 HTML。第二个测试会经由过程将值放到哀求的 URL 中来哀求谋略 10 和 20 的和,然后反省返回的页面中的结果。

这个页面的代码如下所示。

清单 11. TestPage.php

" /> +

" /> =

这个页面相称简单。两个输入域显示了哀求中供给确当前值。结果 span 显示了这两个值的和。 标记标出了所有差别:它对付用户来说是弗成见的,然则对付单元测试来说却是可见的。是以单元测试并不必要繁杂的逻辑来找到这个值。相反,它会检索一个特定标记的值。这样当界面发生变更时,只要 span 存在,测试就可以经由过程。

与前面一样,首先编写测试用例,然后创建一个掉败版本的页面。我们对掉败环境进行测试,然后改动页面的内容使其可以事情。结果如下:

清单 12. 测试掉败环境,然后改动页面

这些测试确保利用编程接口(API)可以精确地分配并开释资本 —— 例如,继续几回调用打开、写入以及关闭基于文件的 API,从而确保没有文件依然是被打开的。

回调测试

对付具有回调措施的 API 来说,这些测试可以确保假如没有定义回调函数,代码可以正常运行。别的,这些测试还可以确保在定义了回调函数然则这些回调函数操作有误或孕育发生非常时,代码依然可以正常运行。

这是有关单元测试的几点设法主见。有关若何编写单元测试,我也有几点建议:

不要应用随机数辉煌煌国际hui137登陆l据

只管在一个界面中孕育发生随机数据看起来貌似一个好主见,然则我们要避免这样做,由于这些数据会变得异常难以调试。假如数据是在每次调用时随机天生的,那么就可能孕育发生一次测试时呈现了差错而别的一次测试却没有呈现差错的环境。假如测试必要随机数据,可以在一个文件中天生这些数据,然后每次运行时都应用这个文件。采纳这种措施,我们就得到了一些 “噪音” 数据,然则仍旧可以对差错进行调试。

分组测试

我们很轻易累积起数千个测试,必要几个小时才能履行完。这没什么问题,然则对这些测试进行分组使我们可以快速运行某组测试并对主要关注的问题进行反省,然后晚上运行完备的测试。

编写稳健的 API 和稳健的测试

编写 API 和测试时要留意它们不能在增添新功能或改动现有功能时很轻易就会崩溃,这一点异常紧张。这里没有通用的绝招,然则有一条准则是那些 “振荡的” 测试(一下子掉败,一下子成功,反复不绝的测试)应该很快地丢弃。

停止语

单元测试对付工程师来说意义重大年夜。它们是敏捷开拓历程(这个历程异常强调编码的感化,由于文档必要一些证据证实代码是按照规范进行事情的)的一个根基。单元测试就供给了这种证据。这个历程从单元测试开始入手,这定义了代码应该 实现但今朝尚未 实现的功能。是以,所有的测试最初都邑掉败。然后现代码靠近完成时,测试就经由过程了。当所有测试整个经由过程期,代码也就变得异常完善了。

我从来没有在不应用单元测试的环境下辉煌煌国际hui137登陆l编写大年夜型代码或改动大年夜型或繁杂的代码块。我平日都是在改动代码之前就为现有代码编写了单元测试,这样可以确保自己清楚在改动代码时破坏了什么(或者没有破坏什么)。这为我对自己供给给客户的代码供给了很大年夜的信心,信托它们正在精确运行 —— 即就是在早晨 3 点。

免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。

您可能还会对下面的文章感兴趣: