Hello World

Imagine you are working on a high sophisticated application called Hello World. In the age of Web 2.0 this application needs needs to flexible by design and friendly to the whole world.

Anyway, you read the specifications and you came up with a table that sort of describes the requirements. Of course, you always create HTML table:

<table border="1">
    <tr>
        <td>Tutorial.HelloWorld</td>
    </tr>
    <tr>
        <td>sayHello()</td>
    </tr>
    <tr>
        <td>Hello World</td>
    </tr>
</table>
Tutorial.HelloWorld
sayHello()
Hello World

Viewed in a browser your document looks pretty mich like the table above. What you had in mind was, that there is a module Tutorial.HelloWorld which can "say Hello" and produces the output "Hello World". you verified this with your customer (who was happy) and implemented you first version of Hello World. Piece of cake!

<?php
class HelloWorld
{
   /**
    * old school greeting
    * @return string
    */
   public function getGreeting()
   {
      return "Hello World";
   }
}
?>

To satisfy the QA department and because you wanted to check out FIT, you added a testing class as well

<?php
class Tutorial_HelloWorld extends Testing_FIT_Fixture_Column
{
    /**
     * @var Greeter
     */
    protected $greeter;
 
    /**
     * constructor
     *
     * Init application class
     */
    public function __construct()
    {
        $this->greeter = new HelloWorld();
    }
 
    /**
     * get greeting string from greeter
     *
     * @return string
     */
    public function sayHello()
    {
        return $this->greeter->getGreeting();
    }
}
?>

To run the test you just have to feed the FIT runner with the HTML table. Then the framework will automaticalle instanciate Tutorial_HelloWord, call sayHello() and compare its return value with Hello World. On success, the correspondig cell of the HTML table will marked green. In case you don't know how to run a test, see How to use the test runner.

Hello World Improved a Little

Your customer was very happy with the software you delievered. However, he came back with a slighly modified table of requirements:

Tutorial.HelloWorld
greeting sayHello(
  Hello World
G'day G'day
  G'day
Hi there Hi there

What they actually wanted was to change the greeting message. No a big deal and you were able to deliver the new version on the same day:

<?php
class HelloWorld
{
    /**
     * greeting text
     * @var string
     */
    protected $greeting = ''Hello World'';
 
    /**
     * old school greeting
     * @return string
     */
    public function getGreeting()
    {
        return $this->greeting;
    }
 
    /**
     * set greeting string
     *
     * @param string $greeting new greeting
     * @return string
     */
    public function setGreeting( $greeting )
    {
        $old = $this->greeting;
        $this->greeting = $greeting;
        return $old;
    }
}
?>

Of course you enhanced you test class and FIT table, as well:

<table border="1">
    <tr>
        <td colspan="2">Tutorial.HelloWorld</td>
    </tr>
    <tr>
        <td>greeting</td>
        <td>sayHello()</td>
    </tr>
    <tr>
        <td>&nbsp;</td>
        <td>Hello World</td>
    </tr>
    <tr>
        <td>G''day</td>
        <td>G''day</td>
    </tr>
    <tr>
        <td>&nbsp;</td>
        <td>G''day</td>
    </tr>
    <tr>
        <td>Hi there</td>
        <td>Hi there</td>
    </tr>
</table>
<?php
class Tutorial_HelloWorld extends Testing_FIT_Fixture_Column
{
    /**
     * @var Greeter
     */
    protected $greeter;
 
    /**
     * @var string
     */
    public $greeting = '''';
 
    /**
     * constructor
     *
     * Init application class
     */
    public function __construct()
    {
        $this->greeter =   new HelloWorld();
    }
 
    /**
     * get greeting string from greeter
     *
     * @return string
     */
    public function sayHello()
    {
        if( !empty( $this->greeting ) ) {
            $this->greeter->setGreeting( $this->greeting );
        }
 
        return $this->greeter->getGreeting();
    }
}
?>

What Happens Inside

As you know, FIT works on tables. In other words, the framework - or the test-runner - can read HTML tables and treats each table as a particular test. Our simple test document contains just one table, or one test case. The first row of such a test case table always contains the so called fixture. Tuturial.HelloWorld in this case. This string tells FIT to load (and create an object of) the test class Tutorial_HelloWorld. The further processing depends on the kind of the test class.

The Tutorial_HelloWorld is a Column Fiture (as it inherits from Testing_FIT_Fixture_Column). Column Fixture means that each column of the table is bound to either a property or method of the testing class. In this case, there is only one property greeting and one method sayHello used. The parenthesis are the hint that tell FIT sayHello() is a method and not a property. Though, after reading the second row, FIT knows how to bind each column.

The actual test scenario starts from from the third row. As the first column belongs to greeting the public member varaible greeting is set to whatever string this cell contains. The second column orders to call sayHello() and compare the return value with whatever string this cell contains. In case both are equal, this test succeeded and this cell is marked as right and appears greenish in the output.

All further rows are processed the same way: set properties, call methods, compare return value and mark corresponnding cell as right or wrong. This is the secret of a Column Fixture.

Further Enhancements

As the customer want to implement a variaty of greetings, the application class must now hold a list of strings. Furhtermore additional common phrases should be covered - like saying "good bye" etc.

Tutorial.HelloWorld
item sayHello() sayBye() ask() answer()
  Hello World Good bye How are you? Good, thanks!
2 G'day See you later How is it going Yeah alright!

See the source code how further improvements got realized.