一、分组测试
分组测试
运行测试用例作为分组的一部分,测试用例应该被放在没有运行码的文件中:
<?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