PHP程序员站--PHP编程开发平台
 当前位置:主页 >> PHP高级编程 >> 高级应用 >> 

Php单元测试工具simpletest介绍

Php单元测试工具simpletest介绍

来源:phperz.com  作者:  发布时间:2010-05-28
一、总览 什么是simpletest? Simpletest的核心是一套建立在测试

一、分组测试

分组测试

运行测试用例作为分组的一部分,测试用例应该被放在没有运行码的文件中:

<?php  

    require_once('../classes/io.php');   
    class FileTester extends UnitTestCase {  
        ...   
    }  

    class SocketTester extends UnitTestCase {   
        ...  
    }   
?>   

尽可能多的用例出现在同一个文件中。他们将包含任何他们需要的代码,包括需要测试的库,但是不包括simpletest库。

如果你已经继承任何测试用例,在php 4中,你能这么使用:

<?php  
    require_once('../classes/io.php');  
    class MyFileTestCase extends UnitTestCase {   
        ...  

    }   
    SimpleTest::ignore('MyFileTestCase');     
    class FileTester extends MyFileTestCase { ... } 

    class SocketTester extends UnitTestCase { ... }  
?>  

Ignore告诉测试系统,忽略他的执行。

在php 5中,可以用abstract关键字:

abstract class MyFileTestCase extends UnitTestCase { 

    ... 

} 
class FileTester extends MyFileTestCase { ... }  
class SocketTester extends UnitTestCase { ... } 

下一步,我们创建组测试文件,叫做my_group_test.php。

我们用安全方法添加测试文件:

<?php 
    require_once('simpletest/unit_tester.php'); 

    require_once('simpletest/reporter.php');  
    require_once('file_test.php');     
    $test = &new GroupTest('All file tests');  
    $test->addTestCase(new MyFileTestCase());   
    $test->run(new HtmlReporter());  
?> 

这将在套餐运行之前,实例化测试用例,可以不用逐个去实例化测试用例。

也能按照下面的方法来做:

<?php  
    require_once('simpletest/unit_tester.php');  
    require_once('simpletest/reporter.php');  
    $test = &new GroupTest('All file tests');  
    $test->addTestFile('file_test.php');   
    $test->run(new HtmlReporter()); 

?>  

这儿有2件事情容易产生错误,需要注意:

1. 这个测试文件可能已经被php解析,因此新类将不能被增加。你应该确认测试用例只包含在这个文件中,不在其他文件中。

2.要注意ignore的使用,并且自己派生的测试用例类必须是抽象类,而且必须在GroupTest::addTestFile之前调用ignore。

高级分组

上面的方法是放所有测试用例进一个大的文件。对于大型项目来讲,这有点不够灵活,你可能需要按照某种排序方式分组。

为了获得更大的灵活性,你可以子类化GroupTest,然后按照需要实例化:

<?php 

    require_once('simpletest/unit_tester.php'); 

    require_once('simpletest/reporter.php'); 

    class FileGroupTest extends GroupTest {  
        function FileGroupTest() {  

            $this->GroupTest('All file tests');   
            $this->addTestFile('file_test.php');  

        }  
    }  
?> 

现在我们能从分离的运行文件中调用测试:

<?php  
    require_once('file_group_test.php');  
    $test = &new FileGroupTest();  

    $test->run(new HtmlReporter());   
?> 

或者我们可以对用例进行分组后,进入一个大的分组。我们能自由的混合分组或单个单元测试,只要我们注意双重包含的问题。

<?php 
    $test = &new BigGroupTest('Big group');  
    $test->addTestFile('file_group_test.php');   
    $test->addTestFile('some_test_case.php');  
    $test->run(new HtmlReporter()); 
?> 

二、Mock对象

什么是mock对象?

单独测试一个类或方法里的代码,而不测试里面调用的其他类或方法的代码。即假定调用的其他类或方法都正常执行。

使用Mock Object的场合

实际对象的行为还不确定。

实际的对象创建和初始化非常复杂。

实际对象中存在很难执行到的行为(如网络异常等)。

实际的对象运行起来非常的慢。

实际对象是用户界面程序。

实际对象还没有编写,只有接口等。

创建mock对象

方法与创建服务器存根相同,我们需要的所有事情就是一个已经存在的类。假如说有下面这个数据库类:

class DatabaseConnection {  

    function DatabaseConnection() {   
    }   
    function query() {   
    }  

    function selectQuery() {   
    }   
}  

这个类没有实际实现代码。为了创建这个类的mock版本,我们需要包含mock类库,然后运行生成器:

require_once('simpletest/unit_tester.php');  

require_once('simpletest/mock_objects.php');   
require_once('database_connection.php');  
Mock::generate('DatabaseConnection');   

这生成了一个名为MockDatabaseConnection的克隆类。我们现在能在我们的测试用例中,实例化这个新类。

require_once('simpletest/unit_tester.php');  
require_once('simpletest/mock_objects.php');  
require_once('database_connection.php');  
Mock::generate('DatabaseConnection'); 

class MyTestCase extends UnitTestCase {   
    function testSomething() {   
        $connection = &new MockDatabaseConnection();  

    }   
}   

作为操作者的mock

一个类的mock版本拥有所有方法,因此像$connection->query()这样的调用仍然是合法的。返回值是null,但是我们需要改变他:

$connection->setReturnValue(‘query’, 37)

现在任何时候我们调用$connection->query(),他的返回值都是37。我们能设置任何东西的返回值,一个数据库的hash或者一个持久化对象,都可以。参数在这儿是不相关的,我们总是能得到同样的返回值。

我们也能添加附加的方法到mock对象,也可以选择我们自定义的类名作为mock对象的类名。

Mock::generate('DatabaseConnection', 'MyMockDatabaseConnection', array('setOptions'));   

这儿setOptions()方法被增加。

事情不总是那么简单。迭代就是一个通常的问题,如果在测试时总是返回同一个值,会引起死循环。因此我们需要建立一些值的序列。我们来看看下面的简单的迭代的例子:

class Iterator { 
    function Iterator() {  
    } 

    function next() { 

    } 
}  

这是我们能设想的最简单的迭代。假设迭代总是返回字符串直到到达尾部(当他返回false时),我们能用下面的方法仿真:

Mock::generate('Iterator');  
class IteratorTest extends UnitTestCase() { 
    function testASequence() { 
        $iterator = &new MockIterator();   
        $iterator->setReturnValue('next', false);  
        $iterator->setReturnValueAt(0, 'next', 'First string');   
        $iterator->setReturnValueAt(1, 'next', 'Second string');  

        ...  
    }  
}  

另外一个需要小技巧的情况是重载get()方法。下面是一个保存名-值对的例子:

class Configuration {  
    function Configuration() {  
    } 

    function getValue($key) {  
    } 

}  

这种基本配置往往随着机器的变化而变化,我们可以手工直接配置。虽然所有的数据都来自getValue方法,但是我们需要根据键返回不同的值。很幸运,mock提供这样的过滤系统:

$config = &new MockConfiguration();   
$config->setReturnValue('getValue', 'primary', array('db_host'));  

$config->setReturnValue('getValue', 'admin', array('db_user'));  
$config->setReturnValue('getValue', 'secret', array('db_password'));  

然后我们可以这样调用:

$config->getValue('db_user') 

他将返回“admin”。附加的参数,将尝试和前面的参数进行匹配。

$config->setReturnValue('getValue', false, array('*'));   

$config->setReturnValue('getValue', false);   

是不一样的。第一种情况接受任何单个的参数,第二种情况接受任何数量的参数。 

有三种因素可以被同时使用,时间、参数、是否copy:

$complex = &new MockComplexThing();  
$stuff = &new Stuff();  
$complex->setReturnReferenceAt(3, 'get', $stuff, array('*', 1));  

它将返回$stuff,只有在第三次调用,并且只有在第二个参数设置为1的时候。

最后一个技巧是,一个对象创建另外一个对象,也就是通常说的工厂模式。假设成功调用query后,返回一个记录集,可以通过next不断迭代获取值,直到返回false为止。这听起来有点像恶梦,实际上mock能做这样的事情。

看下面:

Mock::generate('DatabaseConnection'); 
Mock::generate('ResultIterator');  
class DatabaseTest extends UnitTestCase {  
    function testUserFinder() {  
        $result = &new MockResultIterator();  
        $result->setReturnValue('next', false);   
        $result->setReturnValueAt(0, 'next', array(1, 'tom'));   
        $result->setReturnValueAt(1, 'next', array(3, 'dick'));  

        $result->setReturnValueAt(2, 'next', array(6, 'harry'));  
        $connection = &new MockDatabaseConnection();  
        $connection->setReturnValue('query', false);   
        $connection->setReturnReference(  
                'query',  

                $result,  
                array('select id, name from users'));  
        $finder = &new UserFinder($connection);  
        $this->assertIdentical( 
                $finder->findNames(), 
                array('tom', 'dick', 'harry'));  
    }  
} 

三、部分mock

参看:http://www.lastcraft.com/partial_mocks_documentation.php

四、输出报告

Simpletest非常好的遵循mvc模式。报告类是显示层。

在html中输出结果

详查HtmlReporter类的使用。

命令行输出结果

详查TextReporter类

<?php  
    require_once('simpletest/unit_tester.php');  
    require_once('simpletest/reporter.php');  
    $test = &new GroupTest('File test');  
    $test->addTestFile('tests/file_test.php'); 
    $test->run(new TextReporter());  
?>  

远程测试

SimpleTestXmlParser

<?php  
    require_once('simpletest/xml.php');  
    ... 
    $parser = &new SimpleTestXmlParser(new HtmlReporter()); 

    $parser->parse($test_output); 
?>  

五、期望

六、Web测试器

参看:http://www.lastcraft.com/web_tester_documentation.php

七、测试表单

参看:http://www.lastcraft.com/form_testing_documentation.php

八、验证

参看:http://www.lastcraft.com/authentication_documentation.php

九、可执行脚本的浏览器

参看:http://www.lastcraft.com/browser_documentation.php


延伸阅读:
什么是Xdebug及安装配置方法
什么是simpletest
在PHP中使用SimpleTest进行单元测试
Tags: php   单元测试   工具   Simpletest   测试  
最新文章
推荐阅读
月点击排行榜
PHP程序员站 Copyright © 2007-2010,PHPERZ.COM All Rights Reserved 粤ICP备07503606号