官网 http://simpletest.org/
Php单元测试工具simpletest
一 总览
什么是simpletest
simpletest
是一个用PHP编写的开源项目
这个项目的目的是使PHP开发者
方便快捷的对自己的代码进行单元测试
所有代码用PHP编写
开发者可以根据自己的需要对其经行修改扩展
simpletest 可以
1.单元测试
2.MOCK测试 伪装虚拟测试环境
3.页面测试
测试可以通过WEB应用访问:
也可以用命令行:
二.单元测试
下载源码后 解压到web服务器能访问到的地方
第一个测试程序
第一行 引用simpletest/autorun.php文件 加载所有用到的测试类
以及框架性代码
第三行 创建测试类 这个类要继承自 UnitTestCase
类中的方法 只要以 test 开头就会按顺序自动执行
上面的例子没有实际意义
下面给个有意义的例子
这里引入了一个 user.php的文件 定义了User类
两个测试用例(方法)
function test_user_name() 中 $this->assertIsA($name, 'string'); 用来测试返回的名字 name
是否为一个string类型
function test_user_sex_male()
中 $this->assertTrue($user->isMale());用来测试返回值是否为True
这里我们修改下程序
结果如下
可调用的默认测试用例列表如下
Here is the base list...
assertTrue($x)
Fail unless $x evaluates true
assertFalse($x)
Fail unless $x evaluates false
assertNull($x)
Fail unless $x is not set
assertNotNull($x)
Fail unless $x is set to something
assertIsA($x, $t)
Fail unless $x is the class or type $t
assertNotA($x, $t)
Fail unless $x is not the class or type $t
assertEqual($x, $y)
Fail unless $x == $y is true
assertNotEqual($x, $y)
Fail unless $x == $y is false
assertWithinMargin($x, $y, $margin)
Fail unless $x and $y are separated less than $margin
assertOutsideMargin($x, $y, $margin)
Fail unless $x and $y are sufficiently different
assertIdentical($x, $y)
Fail unless $x === $y for variables, $x == $y for objects of
the same type
assertNotIdentical($x, $y)
Fail unless $x === $y is false, or two objects are unequal or
different types
assertReference($x, $y)
Fail unless $x and $y are the same variable
assertCopy($x, $y)
Fail unless $x and $y are the different in any way
assertSame($x, $y)
Fail unless $x and $y are the same objects
assertClone($x, $y)
Fail unless $x and $y are identical, but separate objects
assertPattern($p, $x)
Fail unless the regex $p matches $x
assertNoPattern($p, $x)
Fail if the regex $p matches $x
expectError($e)
Triggers a fail if this error does not happen before the end of
the test
expectException($e)
Triggers a fail if this exception is not thrown before the end
of the test
具体实现可以查看源码
有的时候上面的方法不能满足需求
比如要及收入一个测试指定文件是否存在的测试用例 这是可以对框架进行扩展
如下
require_once('simpletest/autorun.php');
class FileTester extends UnitTestCase {
function FileTester($name = false) {
$this->UnitTestCase($name);
}
function assertFileExists($filename, $message = '%s') {
$this->assertTrue(
file_exists($filename),
sprintf($message, 'File [$filename] existence check'));
}
}
但纯的这样写 simpletest 会把这个类当做测试用例来处理
这种情况两种处理方法
1.将这个类变为 抽象类 abstract
2.SimpleTestOptions::ignore('FileTester'); 将这段代码嵌入程序中
之后就可以用自己编写的测试用例了
class FileTestCase extends FileTester {
function setUp() {
@unlink('../temp/test.txt');
}
function tearDown() {
@unlink('../temp/test.txt');
}
function testCreation() {
$writer = &new FileWriter('../temp/test.txt');
$writer->write('Hello');$this->assertFileExists('../temp/test.txt');
}
}
除了写test方法 调用测试用例外
还可以继承/使用 UnitTestCase
一些框架性函数
setUp()
Runs this before each test method
tearDown()
Runs this after each test method
pass()
Sends a test pass
fail()
Sends a test failure
error()
Sends an exception event
signal($type, $payload)
Sends a user defined message to the test reporter
dump($var)
Does a
formattedprint_r()for
quick and dirty debugging
三.组测试
上面是一个个测试 我们可以把很多测试用例集合在一起形成一个测试组
继承类
TestSuite
代码:
也可以用
来收集指定文件下 的测试用例
四.Mock Test /Mock Object
有的时候测试需要搭建一个很大的测试环境
比如说 数据库 我们需要读取 / 修改 数据库数据 这样要先建立数据库,插入测试数据才能进行测试
这样mock test 就诞生了 mock test 虚拟的环境代替真实环境 用较小的代价实现测试目的
比如
数据库连接
class DatabaseConnection {
function DatabaseConnection() { }
function query($sql) { }
function selectQuery($sql) { }
}
下面用simpletest 建立一个mock object:
这个方法创建一个假的数据库连接类
class MockDatabaseConnection extends DatabaseConnection {
public $mock;
protected $mocked_methods = array('databaseconnection', 'query', 'selectquery');
function MockDatabaseConnection() {
$this->mock = new SimpleMock();
$this->mock->disableExpectationNameChecks();
}
...
function DatabaseConnection() {
$args = func_get_args();
$result = &$this->mock->invoke("DatabaseConnection", $args);
return $result;
}
function query($sql) {
$args = func_get_args();
$result = &$this->mock->invoke("query", $args);
return $result;
}
function selectQuery($sql) {
$args = func_get_args();
$result = &$this->mock->invoke("selectQuery", $args);
return $result;
}
}
使用
require_once('simpletest/autorun.php');
require_once('database_connection.php');
Mock::generate('DatabaseConnection');
class MyTestCase extends UnitTestCase {
function testSomething() {
$connection = new MockDatabaseConnection();
}
}
我们可以修改其中的返回值
$connection->returns('query', 37)//所有调用query 均返回37
require_once('simpletest/autorun.php');
require_once('database_connection.php');
Mock::generate('DatabaseConnection');
或者
class Iterator {
function Iterator() { }
function next() { }
}
Mock::generate('Iterator');
class IteratorTest extends UnitTestCase() {
function testASequence() {$iterator = new MockIterator();
$iterator->returns('next', false); //所有调用返回
$iterator->returnsAt(0, 'next', 'First string');//第一次调用返回
$iterator->returnsAt(1, 'next', 'Second string');//第二次调用返回
...
}
}
$config = &new MockConfiguration();
$config->returns('get', 'primary', array('db_host')); //第一个参数 是db_host 返回 primary
$config->returns('get', 'admin', array('db_user'));
$config->returns('get', 'secret', array('db_password'));
五.页面测试
单元测试固然重要
但WEB开发最终还是看页面
所有的WEB页面实际上是一堆 数据 这样使简单的WEB测试成为可能
class TestOfLastcraft extends WebTestCase {
}
class TestOfLastcraft extends WebTestCase {
function testHomepage() {
$this->assertTrue($this->get('http://www.lastcraft.com/'));
}
}
get方法 如果页面有返回内容 则返回true 注意 类似404 get方法也会返回true 次方法是获取页面的入口方法 必须首先调用
class TestOfLastcraft extends WebTestCase {
function testHomepage() {$this->get('http://www.lastcraft.com/');
$this->assertText('Why the last craft');//判断是否包含这个字符串
}
}
Here is the list of possible content assertions...
assertTitle($title)
Pass if title is an exact match
assertText($text)
Pass if matches visible and "alt" text
assertNoText($text)
Pass if doesn't match visible and "alt" text
assertPattern($pattern)
A Perl pattern match against the page content
assertNoPattern($pattern)
A Perl pattern match to not find content
assertLink($label)
Pass if a link with this text is present
assertNoLink($label)
Pass if no link with this text is present
assertLinkById($id)
Pass if a link with this id attribute is present
assertNoLinkById($id)
Pass if no link with this id attribute is present
assertField($name, $value)
Pass if an input tag with this name has this value
assertFieldById($id, $value)
Pass if an input tag with this id has this value
assertResponse($codes)
Pass if HTTP response matches this list
assertMime($types)
Pass if MIME type is in this list
assertAuthentication($protocol)
Pass if the current challenge is this protocol
assertNoAuthentication()
Pass if there is no current challenge
assertRealm($name)
Pass if the current challenge realm matches
assertHeader($header, $content)
Pass if a header was fetched matching this value
assertNoHeader($header)
Pass if a header was not fetched
assertCookie($name, $value)
Pass if there is currently a matching cookie
assertNoCookie($name)
Pass if there is currently no cookie of this name
$this->assertMime(array('text/plain', 'text/html'));
$this->assertResponse(200);
交互:
getUrl()
The current location
get($url, $parameters)
Send a GET request with these parameters
post($url, $parameters)
Send a POST request with these parameters
head($url, $parameters)
Send a HEAD request without replacing the page content
retry()
Reload the last request
back()
Like the browser back button
forward()
Like the browser forward button
authenticate($name, $password)
Retry after a challenge
restart()
Restarts the browser as if a new session
getCookie($name)
Gets the cookie value for the current context
ageCookies($interval)
Ages current cookies prior to a restart
clearFrameFocus()
Go back to treating all frames as one page
clickSubmit($label)
Click the first button with this label
clickSubmitByName($name)
Click the button with this name attribute
clickSubmitById($id)
Click the button with this ID attribute
clickImage($label, $x, $y)
Click an input tag of type image by title or alt text
clickImageByName($name, $x, $y)
Click an input tag of type image by name
clickImageById($id, $x, $y)
Click an input tag of type image by ID attribute
submitFormById($id)
Submit a form without the submit value
clickLink($label, $index)
Click an anchor by the visible label text
clickLinkById($id)
Click an anchor by the ID attribute
getFrameFocus()
The name of the currently selected frame
setFrameFocusByIndex($choice)
Focus on a frame counting from 1
setFrameFocus($name)
Focus on a frame by name
请求修改
getTransportError()
The last socket error
showRequest()
Dump the outgoing request
showHeaders()
Dump the incoming headers
showSource()
Dump the raw HTML page content
ignoreFrames()
Do not load framesets
setCookie($name, $value)
Set a cookie from now on
addHeader($header)
Always add this header to the request
setMaximumRedirects($max)
Stop after this many redirects
setConnectionTimeout($timeout)
Kill the connection after this time between bytes
useProxy($proxy, $name, $password)
Make requests via this proxy URL