Doctests for php - test & create documentation

Posted: March 1st, 2009 | Author: sofia | Filed under: useful | Tags: |

Recently I found out about doctests in php. Doctests are tests you can put inside the docblock of the class or method. They originated in python but now there’s a ruby implementation, a php implementation and even a javascript implementation

An example will explain better (applies to the php package):

class simplemath

{

/**
* subtract function.
*
* @access public
* @param int $a
* @param int $b
* @return int
*
* <code>
* //doctest: subtract
* echo simplemath::subtract(10,6);
* //expects:
* //4
* </code>
*/
public static function subtract($a, $b)
{
return (int)$a - (int)$b;
}

}

The test itself is inside the <code> tags. If I then run this code through doctest’s commandline runner for php, by running

$ phpdt simplemath.php

I’ll get something like this:

Doctests can serve as great documentation by example and as tests, both for your documentation - comments tend to get terribly out of date (when code changes the comments aren’t updated accordingly) - and for the code itself.

I’ve also found they’re an invaluable tool during development. Imagine you’re developing a method that validates credit card numbers and the only way to get to it in a browser is to perform several actions, eg going to the specific webpage, inputting the credit card number, and submitting the form - it’s hell basically. If you’re already doing TDD and using phpUnit you’re probably not doing this but doctest for me has an advantage there, the test and the code being tested are in the same file - it’s all there! and so it’s really easy to grasp the problem at hand.

My process has been,

  • write a test case in doctest
  • write the code itself
  • test
  • repeat as necessary

Due to this I submitted a patch to allow the testing of only 1 test, it’s not in the current release but it’s been accepted. This way instead of waiting for all the tests to run, i can run only the method I’m working on.

Another cool thing about it is you become aware of any initial setup code and dependencies. Look at the following:


 /**
* Calculates the number of nights between the start and end date of the booking
* @return int
*

* <code>
* // doctest: booker->total_nights
* require_once('lib/utilities.class.php');
* $booker = new booker();
* $booker->startDate = '2009-02-23';
* $booker->endDate = '2009-02-26';
* echo $booker->total_nights(). "\n";
* // expects:
* // 3
* </code>
*
*/
public function total_nights()
{
if (!isset($this->startDate) || !isset($this->endDate)) {
return false;
}
return (int)utilities::date_diff($this->startDate,$this->endDate,'d');
}

Even without the rest of the class it’s immediately visible that this method requires that the start and end dates be set and the inclusion of another class.

I think doctests are a really easy way to start writing tests. If you’re like me and also use phpUnit, it’s easy to write an AllTests file that runs both the doctest tests and the phpunit tests.

I view doctest as unitestting in its simplest form, there’s no setup/teardown methods, no mocks, etc. But since not all classes need all that, that’s fine by me. You can use doctest and phpunit/simpletest side by side. This way you have tests for code and tests for documentation :)

Doctests pros (more or less from the php-doctest documentation ):

  • it makes unit tests writing easier and quicker -> easy to test as you go
  • it ensures that docstrings are up-to-date by verifying that all examples work as documented -> a sort of testable documentation
  • it enforces writing of tutorial documentation, liberally illustrated with input-output examples.

Cons:

  • If placed inline and used as the only method of unit-testing they can easily clutter the code. 

Wishlist for the php package:

  • Ability to enter into interactive mode inside a doctest (interlude recently added this ability in python) 
  • Allow the inclusion of standalone test files (*) - right now asfaik it only allows for the expected values to be in another file, like this: 


    * <code>
    * //doctest: add
    * echo simplemath::add(3,2);
    * echo simplemath::add(5,2);
    * // expects-file: atestfile.txt
    * </code>

     

Hope it’s as useful to you as it’s been to me :)

* Update: I submitted a patch to allow for this, so if it’s accepted, it will be possible soon.

Share and Enjoy: These icons link to social bookmarking sites where readers can share and discover new web pages.
  • bodytext
  • del.icio.us
  • Mixx
  • Google
  • description


Leave a Reply