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!
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
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:
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> </td> <td>Hello World</td> </tr> <tr> <td>G''day</td> <td>G''day</td> </tr> <tr> <td> </td> <td>G''day</td> </tr> <tr> <td>Hi there</td> <td>Hi there</td> </tr> </table>
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
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.