cvs: peardoc /en/packages/PHPUnit tutorial.xml

From: Date: Fri, 22 Feb 2002 08:35:04 +0000
Subject: cvs: peardoc /en/packages/PHPUnit tutorial.xml
Groups: php.pear.cvs 
Request: Send a blank email to pear-cvs+get-2554@lists.php.net to get a copy of this message
alexmerz Fri Feb 22 03:35:04 2002 EDT Added files: /peardoc/en/packages/PHPUnit tutorial.xml Log: appetizer :-)

Index: peardoc/en/packages/PHPUnit/tutorial.xml +++ peardoc/en/packages/PHPUnit/tutorial.xml <?xml version="1.0" encoding="iso-8859-1" ?> <!-- $Revision: 1.1 $ --> <refentry id="packages.phpunit.tutorial"> <refnamediv> <refname>Tutorial</refname> <refpurpose> A short introduction to the test framework </refpurpose> </refnamediv> <refsect1 id="packages.phpunit.tutorial.purpose"> <title>Why using the test framework?</title> <para> <classname>PHPUnit</classname> gives you a simple framework for creating a test application to automate testing of functions and classes. PHPUnit is inspired by <ulink url="http://www.junit.org">JUnit</ulink>. Kent Beck and Erich Gamma created JUnit as a tool for <ulink url="http://xprogramming.com"> eXtreme Programming</ulink>. One of the parts of XP is to test small software components as often and early as possible and not to fix bugs and errors in the API while setting up and testing the whole application. You have not to switch to XP to benefit from PHPUnit. It is a good tool for testing classes or a set of functions and helps you to avoid endless debug sessions. </para> </refsect1> <refsect1 id="packages.phpunit.tutorial.workroutine"> <title>Work routine</title> <para> Normal, you would write a class, do some unsystematic tests using <function>echo</function> or <function>var_dump</function>. After this, you use the class in your application and hope everything is ok. To benefit from PHPUnit you should rethink the flow. The best way is to do this: <itemizedlist> <listitem> <para>1. design your class/API</para> </listitem> <listitem> <para>2. create a test suite</para> </listitem> <listitem> <para>3. implement the class/API</para> </listitem> <listitem> <para>4. run the test suite</para> </listitem> <listitem> <para>5. fix failures or errors and go to #4 again</para> </listitem> </itemizedlist> This seems to require a lot of time, but this impression is wrong. Creating the test suite using PHPUnit needs only a few minutes and running the test suite only seconds. </para> </refsect1> <refsect1 id="packages.phpunit.tutorial.design"> <title>Design a class</title> <para> Let's start with a small example: a string class. First we create a bunch of functions declarations to work on a string: <programlisting role="php"> <![CDATA[ ---- string.php ---- <?php class String { //contains the internal data var $data; // constructor function String($data) { $this->data = $data; } // creates a deep copy of the string object function copy() { } // adds another string object to this class function add($string) { } // returns the formated string function toString($format) { } } ?> ]]> </programlisting> </para> </refsect1> <refsect1 id="packages.phpunit.tutorial.testsuite"> <title>Creating test suite</title> <para> Now we can create a test suite, which checks every function of your string class. A test suite is normal PHP class inherited from <classname>PHPUnit_TestCase </classname> containing test functions, identified by a leading 'test' in the function name. In the test function an expected value has to be compared with the result of the function to test. The result of this compare must delegate to a function of the <function>assert*</function>-family, which decides if a function pass or fail the test. <programlisting role="php"> <![CDATA[ ---- testcase.php ---- <?php require_once 'string.php'; require_once 'PHPUnit/PHPUnit.php' class StringTest extends PHPUnit_TestCase { // contains the object handle of the string class var $abc; // constructor of the test suite function StringTest($name) { $this->PHPUnit_TestCase($name); } // called before the test functions will be executed // this function is defined in PHPUnit_TestCase and overwritten // here function setUp() { // create a new instance of String with the // string 'abc' $this->abc = new String("abc"); } // called after the test functions are executed // this function is defined in PHPUnit_TestCase and overwritten // here function tearDown() { // delete your instance unset($this->abc); } // test the toString function function testToString() { $result = $this->abc->toString('contains %s'); $expected = 'contains abc'; $this->assertTrue($result == $expected); } // test the copy function function testCopy() { $abc2 = $this->abc->copy(); $this->assertEquals($abc2, $this->abc); } // test the add function function testAdd() { $abc2 = new String('123'); $this->abc->add($abc2); $result = $this->abc->toString("%s"); $expected = "abc123"; $this->assertTrue($result == $expected); } } ?> ]]> </programlisting> </para> </refsect1> <refsect1 id="packages.phpunit.tutorial.firstrun"> <title>The first test run</title> <para> Now, we can run a first test. Execute this PHP program. Make sure that the paths are correct. <programlisting role="php"> <![CDATA[ ---- stringtest.php ---- <?php require_once 'testcase.php'; require_once 'PHPUnit/PHPUnit.php'; $suite = new PHPUnit_TestSuite("StringTest"); $result = PHPUnit::run($suite); echo $result -> toString(); ?> ]]> </programlisting> </para> <para> If you call this script through commandline, you will get this output: <programlisting> <![CDATA[ TestCase stringtest->testtostring() failed: expected true, actual false TestCase stringtest->testcopy() failed: expected , actual Object TestCase stringtest->testadd() failed: expected true, actual false ]]> </programlisting> Every function fails the test, because your string functions didn't returned what we defined as the expected value. </para> <para> If you want to call the script through your browser, you have to put the script in a correct html page and call <function>$result->toHTML </function> instead of <function>$result->toString</function>. </para> </refsect1> <refsect1 id="packages.phpunit.tutorial.implement"> <title>Implementation</title> <para> Ok, let's start with implementation of the our string class. <programlisting role="php"> <![CDATA[ ---- string.php ---- <?php class String { //contains the internal data var $data; // constructor function String($data) { $this->data = $data; } // creates a deep copy of the string object function copy() { $ret = new String($this->data); return $ret; } // adds another string object to this class function add($string) { $this->data = $this->data.$string->toString("%ss"); } // returns the formated string function toString($format) { $ret = sprintf($format, $this->data); return $ret; } } ?> ]]> </programlisting> </para> <para> Implementing is done and run the test again: <programlisting> ~> <command>php -f stringtest.php</command> TestCase stringtest->testtostring() passed TestCase stringtest->testcopy() passed TestCase stringtest->testadd() failed: expected true, actual false </programlisting> D'oh! the last test failed! We did a typing mistake. Change line 16 in <filename>string.php</filename> to <programlisting role="php"> <![CDATA[ $this->data = $this->data.$string->toString("%s"); ]]> </programlisting> and run the test again: <programlisting> ~> <command>php -f stringtest.php</command> TestCase stringtest->testtostring() passed TestCase stringtest->testcopy() passed TestCase stringtest->testadd() passed </programlisting> Everything is ok now! </para> </refsect1> <refsect1 id="packages.phpunit.tutorial.conclusion"> <title>Conclusion</title> <para> Hm, such a big expenditure for testing three simple functions? Don't forget, this is a small example. Thing about bigger, complexer API's like database abstraction or basket classes in a shop application. PHPUnit is an excellent tool to detect errors in the implementation. Maybe you have a big class used in a few applications and you want to reimplement the class. With a test suite, you can easily check and fix the new implementation in a short session. </para> </refsect1> </refentry> <!-- Keep this comment at the end of the file Local variables: mode: sgml sgml-omittag:t sgml-shorttag:t sgml-minimize-attributes:nil sgml-always-quote-attributes:t sgml-indent-step:1 sgml-indent-data:t sgml-parent-document:nil sgml-default-dtd-file:"../../manual.ced" sgml-exposed-tags:nil sgml-local-catalogs:nil sgml-local-ecat-files:nil End: vim600: syn=xml fen fdm=syntax fdl=2 si vim: et tw=78 syn=sgml vi: ts=1 sw=1 -->
« previous php.pear.cvs (#2554) next »