Your Ad Here
首页 | 编程语言 | 网站建设 | 游戏天堂 | 冲浪宝典 | 网络安全 | 操作系统 | 软件时空 | 硬件指南 | 病毒相关 | IT 认证
软讯网络 > 网站建设 > PHP > Simple Test for PHP ——关于php的测试工具simple test
【标  题】:Simple Test for PHP ——关于php的测试工具simple test
【关键字】:Simple,Test,for,PHP,php,simple,test
【来  源】:http://www.cublog.cn/u/17686/showart.php?id=128475

Simple Test for PHP ——关于php的测试工具simple test

Your Ad Here

Simple Test for PHP

The following assumes that you are familiar with the concept of unit testing as well as the PHP web development language. It is a guide for the impatient new user of SimpleTest. For fuller documentation, especially if you are new to unit testing see the ongoing documentation, and for example test cases see the unit testing tutorial.

Using the tester quickly

Amongst software testing tools, a unit tester is the one closest to the developer. In the context of agile development the test code sits right next to the source code as both are written simultaneously. In this context SimpleTest aims to be a complete PHP developer test solution and is called "Simple" because it should be easy to use and extend. It wasn't a good choice of name really. It includes all of the typical functions you would expect from JUnit and the PHPUnit ports, but also adds mock objects. It has some JWebUnit functionality as well. This includes web page navigation, cookie testing and form submission.

The quickest way to demonstrate is with an example.

Let us suppose we are testing a simple file logging class called Log in classes/log.php. We start by creating a test script which we will call tests/log_test.php and populate it as follows...

<?php
require_once('simpletest/unit_tester.php');
require_once('simpletest/reporter.php');
require_once('../classes/log.php');
?>
Here the simpletest folder is either local or in the path. You would have to edit these locations depending on where you placed the toolset. Next we create a test case...
<?php
require_once('simpletest/unit_tester.php');
require_once('simpletest/reporter.php');
require_once('../classes/log.php');

class TestOfLogging extends UnitTestCase {
}
?>
Now we have five lines of scaffolding code and still no tests. However from this part on we get return on our investment very quickly. We'll assume that the Log class takes the file name to write to in the constructor and we have a temporary folder in which to place this file...
<?php
require_once('simpletest/unit_tester.php');
require_once('simpletest/reporter.php');
require_once('../classes/log.php');

class TestOfLogging extends UnitTestCase {
    
    function testCreatingNewFile() {
        @unlink('/temp/test.log');
        $log = new Log('/temp/test.log');
        $this->assertFalse(file_exists('/temp/test.log'));
        $log->message('Should write this to a file');
        $this->assertTrue(file_exists('/temp/test.log'));
    }
}
?>
When a test case runs it will search for any method that starts with the string test and execute that method. We would normally have more than one test method of course. Assertions within the test methods trigger messages to the test framework which displays the result immediately. This immediate response is important, not just in the event of the code causing a crash, but also so that print statements can display their content right next to the test case concerned.

To see these results we have to actually run the tests. If this is the only test case we wish to run we can achieve it with...

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

class TestOfLogging extends UnitTestCase {
    
    function testCreatingNewFile() {
        @unlink('/temp/test.log');
        $log = new Log('/temp/test.log');
        $this->assertFalse(file_exists('/temp/test.log'));
        $log->message('Should write this to a file');
        $this->assertTrue(file_exists('/temp/test.log'));
    }
}

$test = &new TestOfLogging();
$test->run(new HtmlReporter());
?>

On failure the display looks like this...

testoflogging

Fail: testcreatingnewfile->True assertion failed.
1/1 test cases complete. 1 passes and 1 fails.
...and if it passes like this...

testoflogging

1/1 test cases complete. 2 passes and 0 fails.
And if you get this...
Fatal error: Failed opening required '../classes/log.php' (include_path='') in /home/marcus/projects/lastcraft/tutorial_tests/Log/tests/log_test.php on line 7
it means you're missing the classes/Log.php file that could look like...
<?php
class Log {
    
        function Log($file_path) {
        }

		function message() {
		}
}
?>;

Building group tests

It is unlikely in a real application that we will only ever run one test case. This means that we need a way of grouping cases into a test script that can, if need be, run every test in the application.

Our first step is to strip the includes and to undo our previous hack...

<?php
require_once('../classes/log.php');

class TestOfLogging extends UnitTestCase {
    
    function testCreatingNewFile() {
        @unlink('/temp/test.log');
        $log = new Log('/temp/test.log');
        $this->assertFalse(file_exists('/temp/test.log'));
        $log->message('Should write this to a file');
        $this->assertTrue(file_exists('/temp/test.log'));
    }
}
?>
Next we create a new file called tests/all_tests.php and insert the following code...
<?php
require_once('simpletest/unit_tester.php');
require_once('simpletest/reporter.php');

$test = &new GroupTest('All tests');
$test->addTestFile('log_test.php');
$test->run(new HtmlReporter());
?>
The method GroupTest::addTestFile() will include the test case file and read any new classes created that are descended from SimpleTestCase, of which UnitTestCase is one example. Just the class names are stored for now, so that the test runner can instantiate the class when it works its way through your test suite.

For this to work properly the test case file should not blindly include any other test case extensions that do not actually run tests. This could result in extra test cases being counted during the test run. Hardly a major problem, but to avoid this inconvenience simply add a SimpleTestOptions::ignore() directive somewhere in the test case file. Also the test case file should not have been included elsewhere or no cases will be added to this group test. This would be a more serious error as if the test case classes are already loaded by PHP the GroupTest::addTestFile() method will not detect them.

To display the results it is necessary only to invoke tests/all_tests.php from the web server.

Using mock objects

Let's move further into the future.

Assume that our logging class is tested and completed. Assume also that we are testing another class that is required to write log messages, say a SessionPool. We want to test a method that will probably end up looking like this...


class SessionPool {
    ...
    function logIn($username) {
        ...
        $this->_log->message("User $username logged in.");
        ...
    }
    ...
}

In the spirit of reuse we are using our Log class. A conventional test case might look like this...

<?php
require_once('../classes/log.php');
require_once('../classes/session_pool.php');

class TestOfSessionLogging extends UnitTestCase {
    
    function setUp() {
        @unlink('/temp/test.log');
    }
    
    function tearDown() {
        @unlink('/temp/test.log');
    }
    
    function testLogInIsLogged() {
        $log = new Log('/temp/test.log');
        $session_pool = &new SessionPool($log);
        $session_pool->logIn('fred');
        $messages = file('/temp/test.log');
        $this->assertEqual($messages[0], "User fred logged in.\n");
    }
}
?>
This test case design is not all bad, but it could be improved. We are spending time fiddling with log files which are not part of our test. Worse, we have created close ties with the Log class and this test. What if we don't use files any more, but use ths syslog library instead? Did you notice the extra carriage return in the message? Was that added by the logger? What if it also added a time stamp or other data?

The only part that we really want to test is that a particular message was sent to the logger. We reduce coupling if we can pass in a fake logging class that simply records the message calls for testing, but takes no action. It would have to look exactly like our original though.

If the fake object doesn't write to a file then we save on deleting the file before and after each test. We could save even more test code if the fake object would kindly run the assertion for us.

Too good to be true? Luckily we can create such an object easily...
<?php
require_once('../classes/log.php');
require_once('../classes/session_pool.php');
Mock::generate('Log');

class TestOfSessionLogging extends UnitTestCase {
    
    function testLogInIsLogged() {
        $log = &new MockLog($this);
        $log->expectOnce('message', array('User fred logged in.'));
        $session_pool = &new SessionPool($log);
        $session_pool->logIn('fred');
        $log->tally();
    }
}
?>
The tally() call is needed to tell the mock object that time is up for the expected call count. Without it the mock would wait forever for the method call to come in without ever actually notifying the test case. The other test will be triggered when the call to message() is invoked on the MockLog object. The mock call will trigger a parameter comparison and then send the resulting pass or fail event to the test display. Wildcards can be included here too so as to prevent tests becoming too specific.

The mock objects in the SimpleTest suite can have arbitrary return values set, sequences of returns, return values selected according to the incoming arguments, sequences of parameter expectations and limits on the number of times a method is to be invoked.

For this test to run the mock objects library must have been included in the test suite, say in all_tests.php.

Web page testing

One of the requirements of web sites is that they produce web pages. If you are building a project top-down and you want to fully integrate testing along the way then you will want a way of automatically navigating a site and examining output for correctness. This is the job of a web tester.

The web testing in SimpleTest is fairly primitive, there is no JavaScript for example. To give an idea here is a trivial example where a home page is fetched, from which we navigate to an "about" page and then test some client determined content.

<?php
require_once('simpletest/web_tester.php');
require_once('simpletest/reporter.php');

class TestOfAbout extends WebTestCase {
    
    function setUp() {
        $this->get('http://test-server/index.php');
        $this->clickLink('About');
    }
    
    function testSearchEngineOptimisations() {
        $this->assertTitle('A long title about us for search engines');
        $this->assertWantedPattern('/a popular keyphrase/i');
    }
}
$test = &new TestOfAbout();
$test->run(new HtmlReporter());
?>
With this code as an acceptance test you can ensure that the content always meets the specifications of both the developers and the other project stakeholders.
关于Smarty学习:【上一篇】
Cache_Lite——缓存:【下一篇】
【相关文章】
  • LINUX下PHP连接MSSQL SERVER的办法
  • nforce 4主板网卡问题(待验证)
  • php自带的简易的unit函数,可以代替echo调试,但是意义好象不太大
  • C#下WinForm编程:登录窗体的设计
  • C#里for和foreach的区别
  • Deep Thinking in Patterns--简单工厂模式(Simple Factory)
  • StrutsTestCase 之discovery
  • 优化详解----深入优化 for C/C++
  • PHP生成雪花背景的验证码
  • PHPBB论坛安装
  • 【随机文章】
  • 广告文案写作所要注意的...
  • SCSI硬盘低级格式化
  • Windows API函数大全二
  • 内存故障现象三
  • 一、Servlet和JSP概述
  • [翻译] Zend框架和PRADO成功组合
  • 使用VB6.0在Windows Vista下实现全磨砂玻璃窗口
  • Windows XP系统中玩虚拟光驱要慎重
  • 饶颖日记爆赵忠祥性虐待细节(一)
  • 导出QQWry.Dat中IP地址到文件
  • 【相关评论】
    没有相关评论
    【发表评论】
    姓名:
    邮件:
    随机码*
    评论*
          
    |  首 页  |  版权声明  |  联系我们   |  网站地图  |
    CopyRight © 2004-2007 软讯网络 All Rigths Reserved.