在这里总结的许多编程惯用法都是很值得做为单独一个章节的甚至一本书的你应该把这章做为PHP模式设计使用惯用法的相关介绍而且查看一些列出的参考书来进行更深入的学习
测试你的代码
可能没有什么代码惯用法比测试代码更加重要了好的测试可以提高开发速度
可能一开始这句格言会和你的直觉相矛盾你可能会断言测试是自由的障碍物事实上恰恰相反如果你十分完整的运行那些测试来检查你的软件的公共接口你就可能在不改变(或者更加糟糕破坏)原来的应用软件的前提下改变自己系统内在的执行测试并检验你的公共接口的精确性和正确性并且让自己随意改变一些代码的内在工作来确保你的软件是正确而且没有bug(错误)
在讨论更多关于测试的好处之前先让我们看一个示例这本书里面所有的测试实例都使用了PHP测试框架——SimpleTest 这个测试框架可以在 ;获取到
考虑下面的代码
<?php
// PHP
// the subject code
define(TAX_RATE );
function calculate_sales_tax($amount) {
round($amount * TAX_RATE);
}
// include test library
require_once simpletest/unit_testerphp;
require_once simpletest/reporterphp;
// the test
class TestingTestCase extends UnitTestCase {
function TestingTestCase($name=) {
$this>UnitTestCase($name);
}
function TestSalesTax() {
$this>assertEqual( calculate_sales_tax());
}
}
// run the test
$test = new TestingTestCase(Testing Unit Test);
$test>run(new HtmlReporter());
上面的代码首先定义了一个常量——TAX_RATE和一个计算销售税的函数接着代码包含了使用SimpleTest框架的必备组件单体测试本身和一个用来显示测试结果的reporter模块
类TestingTestCase继承于SimpleTest框架的UnitTestCase类通过扩展UnitTestCase类TestingTestCase里面所有使用Test开头的方法都将被认为是测试实例——创造条件来调试你的代码并断言结果
TestingTestCase定义了一个测试TestSalesTax()它包含了一个断言函数AssertEqual()如果它的前两个输入参数是相等的它将返回true否则返回false(如果你想显示assertEqual()失败的信息你可以传入三个参数就像这样$this>assertEqual(calculate_sales_tax() The sales tax calculation failed))
代码的最后两行创建了这个测试实例的实体并且使用一个HtmlReporter运行了它你可以访问这个web页面来运行这个简单的测试
运行这个测试将显示测试名称失败断言的详细情况和一个总结条(绿色的意味着成功(所有的断言都通过了)而红色的暗示着失败(至少有一个断言没有通过))
(assertion(断言)在软件开发中是一种常用的调试方式很多开发语言中都支持这种机制在实现中assertion就是在程序中的一条语句它对一个boolean表达式进行检查一个正确程序必须保证这个boolean表达式的值为true如果该值为false说明程序已经处于不正确的状态下系统将给出警告或退出一般来说assertion用于保证程序最基本关键的正确性assertion检查通常在开发和测试时开启为了提高性能在软件发布后assertion检查通常是关闭的)
注(assertion(断言)在软件开发中是一种常用的调试方式很多开发语言中都支持这种机制在实现中assertion就是在程序中的一条语句它对一个boolean表达式进行检查一个正确程序必须保证这个boolean表达式的值为true如果该值为false说明程序已经处于不正确的状态下系统将给出警告或退出一般来说assertion用于保证程序最基本关键的正确性assertion检查通常在开发和测试时开启为了提高性能在软件发布后assertion检查通常是关闭的)
上面的代码有一个(有意的)错误所以运行是不能通过了显示结果如下
Calculate_sales_tax()这么一个简单的才一行的函数哪里出错了呢?你可能已经注意到这个函数没有返回结果下面是正确的函数
function calculate_sales_tax($amount) {
return round($amount * TAX_RATE);
}
修改后运行测试通过
但是一个简单的测试并不能保证代码是稳定的比如你把calculate_sales_tax()改成 function calculate_sales_tax($amount) { return ; }代码也会通过测试但只有当美元等价于的时候才是正确的你可以自己增加一些额外的测试方法来测试其他的静态值
function TestSomeMoreSalesTax() {
$this>assertEqual( calculate_sales_tax());
}
或者改变函数TestSalesTax()来验证第二个(和第三个等等)值如下所示
function TestSalesTax() {
$this>assertEqual( calculate_sales_tax());
$this>assertEqual( calculate_sales_tax());
}
到目前为止还有一种更好的方法就是新增加一个测试选择随即值来测试你的代码具体如下
function TestRandomValuesSalesTax() {
$amount = rand();
$this>assertTrue(defined(TAX_RATE));
$tax = round($amount*TAX_RATE*)/;
$this>assertEqual($tax calculate_sales_tax($amount));
}
TestRandomValuesSalesTax()引入了方法assertTrue()如果传入的第一个变量等于于布尔真则assertTrue()通过(和方法assertEqual()一样方法assertTrue()在接受一个可选择性的额外的后将返回一个失败的信息)所以TestRandomValuesSalesTax()首先认为常量TAX_RATE已经定义了然后使用这个常量来计算随机选择的的数量的税收
但是TestRandomValuesSalesTax()也存在一个问题它很大程度的依赖于方法calculate_sales_tax()测试是应该和特殊的实现细节无关的一个更好的测试应该只建立一个合理的分界线接下来的这个测试假定销售税永远不会超过%
function TestRandomValuesSalesTax() {
$amount = rand();
$this>assertTrue(calculate_sales_tax($amount)<$amount*);
}
确保你的代码正常工作是测试的首要的目的但是在测试你的代码时候你应该认识到除此之外还有一些额外的相对次要的目的
测试让你书写容易测试的代码这使得代码松散耦合复杂设计而且具有很好的模块性
测试能让你清晰的了解运行代码的期望结果让你从一开始就注重于模块的设计和分析通过测试也会让你考虑所有可能的输入和相应的输出结果
测试能很快速的了解编码的目的换句话说测试事例扮演着实例和文档的功能准确的展示着如何构建一个类方法等在这本书中我有时候通过一个测试事例来演示代码的期望功能通过读取一个测试方法的声明你可以清楚的了解代码是如何运行的一个测试实例定义在代码在明确惯用法下的运行情况
最后如果你的测试集——测试实例的集合——是非常彻底的而且当所有的测试都通过的时候你可以说你的代码是完备的有趣的是这个观点也恰好是Test Driven Development(测试驱动开发)的特征之一
Test Driven Development(TDD)也被认为是Test First Coding(编码前测试)Test First Coding是一种把测试更提前一步的方法在你写任何代码之前先写好测试你可以从下载到一份很好的简洁的关于TDD的摘要文章同时下载到一本很好的关于策略的入门书——Kent Beck着作的《Test Driven DevelopmentBy Example》(这本书的例子都是用JAVA开发的但其中代码的可读性是很好的而且对主题的介绍和说明都做的很好的)
注敏捷开发(Agile Development)最近单体测试——特别是测绘驱动开发——已经和敏捷开发方法学紧密的联系起来了比如说极限编程(XP)极限编程的焦点关注于快速的反复的发步功能性的代码给客户并把变化的客户需求做为开发过程中的必备部分下面是一些关于学习敏捷编程的在线资源
函数性测试
这本书里面的大部分测试例子都是用来测试面对对象的代码但是所有形式的编程都可以从中得到收获的单体测试框架比如说PHPUnits和SimpleTest也都能很容易的用来测试功能函数的例如上面的SimpleTest例子它就是用来测试calculate_sales_tax()函数的世界各地的程序员们把单体测试用例放到你的函数库里面吧!
我希望经过上面的讨论后你也会被带动起来——测试引导(Test Infected)!(这个术语原创于Erich Gamma详细情况请见文章)就象Gamma所写的那样刚开始你可能会感到测试是很繁琐的但是当你为你的程序搭建好一个广阔的测试集后你将你的代码更加自信!