TrackStar
在这一章中,我们将介绍一个项目任务跟踪系统,给它取了个名字叫TrackStar。目前世界上,已经有很多关于项目管理和问题跟踪的应用程序,我们的基本功能将没有什么与众不同。那么,为什么还要建立呢?事实证明,这种基于用户的应用程序有很多的功能,也是很常见的网络应用,这将使我们能够实现两个主要目的:
• 使用Yii自带的方便快捷的功能,建立可用的功能并挑战已经存在的其他网络应用。
• 介绍真实的示例和设计方案,这将帮助你可以快速建立属于你自己的Web应用。
单元测试和功能测试
单元测试的编写提供了验证代码是否正确。功能测试的编写提供了对应用程序整体的功能是否正
确。
Unit单元测试
单元测试是软件测试中最小的单位,在面向对象的应用程序中,(如Yii应用程序)的最小单位是类的接口,公共的方法。单元测试集中在一个单独的类中,而不要求与其它类或对象一起运行。他们的目的是为了验证一个最小单位的代码是否达到预期目的。
功能测试
功能测试重点测试应用程序端对端的功能特生。这个测试相对于单元测试要高一个层次,通常要多个类或对象一起运行。功能测试的目的是验证一个给定的应用程序功能是否可以正常工作。
测试驱动开发
测试驱动开发(TDD)是一种软件开发方法,它有助于为软件开发创造一个舒适和信心的环境,确保你的测试代码与你的应用程序一起成长,并始终保持最新。它规定在你开始写代码之前先写测试代码。下面是总结的步骤:
1. 开始写一个失败的测试代码。
2. 运行测试代码确定它是失败的。
3. 快速编写你的程序代码,并测试通过。
4. 再次运行测试代码,以确保它确实通过了。
5. 重构代码,移除重复的逻辑或改善某些部分,并试着测试通过。
在Yii中测试
从Yii1.1版本起,Yii已经紧密结合了PHPUnit(http://www.phpunit.de) 和Selenium Remote Control (http://seleniumhq.org/projects/remote-control/) 测试框架。特定的测试框架并没有关于TDD,但非常推荐使用。
当然,你可以使用测试框架测试Yii的PHP代码。然而,Yii已经与前面提到的两个框架紧密集成,所以使事情变的更简单。因为简单,所以我们首要目标是使用Yii的测试功能。
在第2章中,当我们使用yiic webapp命令创建Hello,World应用程序时,我们注意到,我们创建了许多文件和文件夹。以下是自动测试相关的:
Name of folder Use/contents
demo/
protected This contains protected application files
tests/ This contains tests for application
fixtures/ This contains databases fixtures
functional/ This contains functional tests
unit/ This contains unit tests
report/ This contains coverage reports
bootstrap.php The script executed at the very beginning of the tests
phpunit.xml The PHPUnit configuration file
WebTestCase.php The base class for Web-based functional tests
我们将我们的测试文件放到这个主要目录:fixtures,functional,unit。这些报告文件夹用于存储生成的代码覆盖率的报告。
功能测试
与单元测试一样,功能测试也是编写一个PHP类。但它是继承自己CWebTestCase类,而不是CTestCase类。跟据约定,如果我们的测试类名叫AbcTest,其中Abc是被测试的类,则测试类的文件名叫AbcTest.php只存在protected/tests/functional中。
为了运行功能测试,你需要安装Selenium。
安装Selenium
除了PHPUnit,Selenium Remote Control Server (Selenium RC)是为了运行功能测试所需要的。
安装Selenium Rc非常简单。
1. 从http://seleniumhq.org/download/下载Selenium Remote Control Server (Selenium RC)zip文件
2. 解压zip文件到你的系统中。
在解压后的目录中,有几个是基础于客户端的目录和一个包含Selenium RC Server的目录,它的名字有点类似selenium-server-1.0.x/。其中的x是具体的下载版本。启动服务器也非常简单。只要在命令行下进入服务器文件所在目录,运行以下命令:
% java -jar selenium-server.jar它将在命令行下启动服务。
安装PHPUnit
为了继续往下进行单元测试,您需要安装PHPUnit。应该使用Pear安装(获得更多关于Pear的信息,请访问http://pear.php.net),如Mac OS(苹果操作系统)用户,只需如下两条命令:
% sudo pear channel-discover pear.phpunit.de
% sudo pear install phpunit/PHPUnit
安装方法(也适用Window,进入php目录存在pear命令):
sudo apt-get remove phpunit
sudo pear channel-discover pear.phpunit.de
sudo pear channel-discover pear.symfony-project.com
sudo pear channel-discover components.ez.no
sudo pear update-channels
sudo pear upgrade-all
sudo pear install --alldeps phpunit/PHPUnit
sudo pear install --force --alldeps phpunit/PHPUnit
安装后后可能会报错如下:
Fatal error: require_once(): Failed opening required 'PHPUnit/Extensions/SeleniumTestCase.php' (include_path='.;D:\xampp\php\pear') in D:\xampp\htdocs\yii\framework\test\CWebTestCase.php on line 12
缺少一个文件包,我们需要安装上:pear install phpunit/PHPUnit_Selenium
关于PHPUnit测试功能使用已经超出了本书范围,建议你花一些时间阅读一下文档(http://www.phpunit.de/wiki/Documentation),并学习如何编写一个基础本的单元测试。
单元测试
在Yii是中编写单元测试是从框架类CTestCase中继承一个PHP类。跟据约定,这个类被命名为AbcTest,其中Abc可以被替换成试测类的名称。
例如,第2章的示例程序,如果我们要测试Message类,则测试类的名称是MessageTest。这个类是保存在protected/tests/unit/下,文件名是MessageTest.php。
测试类中主要包括测试的操作方法,名字是testXyz,其中Xyz经常与这个测试类相对的类的操作方法名称。
继续MessageController的例子,如果我们测试Helloworld()方法,则在MessageTest类中的测试方法是testHelloWorld()。
开始使用phpunit测试(TDD)
我们首先在yiidemo/protected/tests/unit/下面建立一个测试文件:MessageTest.php(这个文件是对MessageController.php进行测试)并写入如下代码testRepeat()方法是对MessageController.php里面的repeat方法进行测试。
<?php
class MessageTest extends CTestCase {
function testRepeat()
{
}
}
?>
然后$ cd yiidemo/protected/tests目录,执行命令$ phpunit unit/MessageTest.php 。由于我们没有写任何逻辑代码在文件里面,所以最后会出现:
Time: 0 seconds, Memory: 3.00Mb
OK (1 test, 0 assertions)
现在我们开始编写代码,验证返回的字符串是否与传入的字符串相同。
<?php
Yii::import('application.controllers.MessageController');
class MessageTest extends CTestCase{
function testRepeat()
{
$message = new MessageController('MessageTest');
$text = "Hello,Any One Out there?";
$returnMessage = $message->repeat($text);
$this->assertEquals($returnMessage,$text);
}
}
?>
我们建新一个MessageController类的实例,并提供一个用来验证的controllerId 传给构造函数。然后我们定义一个字符串变量$text ,并传给repeat() 方法,并将返回结果保存在函数返回的$returnMessage变量中。
正如我们期望所返回的信息中包含完全相同的字符串。我们使用PHPUnit的API方法assertEquals()用来比较第一个字符串与第二个字符串的结果是否相等(或真或假)。现在这个测试用例已经写完了,让我们试着运行它:
注:一定要记得把MessageController这个类导入进来,不然不能实例化,我们可以使用Yii::import语法来包含MessageController类。
Yii::import方法允许我们快速包含一个定义的类。它不同与include和require,因为它很高效。这个定义类开始导入时其实并没有包含,直到它首次被引用时才包含。多次导入同一个定义类也要比include_once 和require_once快的多。注意:当提到一个Yii框架内定义类,我们不需要导入或包含它,因为所有YIi的核心类已经预先导入了。
如果你刚刚接触TDD,这一切看起来有点奇怪,特别是要考虑所有细节,有时可能很小的细节只是为了实现这样一个普通的方法。这主要是为了帮助你理解TDD的流程和节奏。控制这个细节的大小是TDD的一门艺术。开始的细节可以很小,后面再放大,这样你会感觉很舒服也更加有信心。
提供给开发者几个在测试框架中需要了解的知识。测试结果通过以彩色编码显示。通过测试显示绿色,测试失败显示红色。这种方式也有点像马路上的红绿灯。TDD常常强调:红(Red),绿(Green),重构(Refactor),重复(Repeat)。这是指的基本步骤,如下:
1. 红色:快速添加一个新的测试用例,然后运行,并看到测试失败。
2. 绿色:做一个小小的改动,只要能满足测试通过就可以。
3. 重构:如有必要,删除代码中任何重复的地方,你做这么是否为了让测试能够快速通过,但在其他方面可能也会让你不妥。
4. 重复:重复第1步。