Tutorials Simple - AugustusKling/RudimentaryPhpTest GitHub Wiki
This tutorial shall guide through the creation of the first unit test. A simple string wrapper class will be used for the sake of having an example.
Start off by cloning the entire repository in order to have the test framework available for invocation. Then create a folder for your implementation classes and one for your test classes. It does not matter where the test framework is located and neither does it matter where your code resides. You can find the final result of this tutorial at /samples/Tutorials/Simple/ in case you have trouble following the steps.
Implementing Code Under Test
Create a file to hold your implementation's code and fill it with the following contents:
<?php
class Tutorials_Simple_Implementation_Text {
private $content;
public function __construct($content){
$this->content = $content;
}
public function __toString(){
return $this->content;
}
}
Note that the name of the class does not matter. The example class allows to invoke it with a string and get that string back out – nothing more yet.
It is time for creating a test class, now. A test class will use the class under test, call its methods and compare the return values with known data. When the return values equal the known data, it is assumed that the tested code works correctly. In general one could say the more test classes exist, the more confident you can be that your implementation is correct.
Side note on code coverage: Coverage is a metric for the percentage of your code that is used by your tests. This testing framework does not support this metric because due to the value domain of parameters, coverage is a misleading number. 100% coverage does not guarantee an error free implementation.
Implementing the Test
Create a file to hold the code of the test class:
<?php
// Point this to your code or replace with an autoloader
require_once dirname(__FILE__).'/../Implementation/Text.php';
class Tutorials_Simple_Text extends RudimentaryPhpTest_BaseTest {
public function conversionToNativeStringTest(){
$implementationUnderTest = new Tutorials_Simple_Implementation_Text("Hello, I'm there to be tested.");
$this->assertEquals("Hello, I'm there to be tested.", $implementationUnderTest->__toString());
$this->assertEquals("Hello, I'm there to be tested.", (string)$implementationUnderTest);
$this->assertEquals("Hello, I'm there to be tested.", strval($implementationUnderTest));
}
}
The test class contains one test (the method) and can be invoked by using the test framework. Both, the names of the test class and the test are arbitrary. However it is advised to stick with a common naming schema; here this means that all tests have a Test suffix. It is worth noting that the framework will only run your class if it inherits from RudimentaryPhpTest_BaseTest and it will only execute methods that match the naming schema.
As you might have already understood, the test starts by instantiating the implementation class to wrap the text Hello, I'm there to be tested.. Then it uses assertions to check that the implementation is correct.
Assertions basically compare a known value with a methods return value as stated above. In this case there are 3 assertions to ensure the text can be unwrapped – one for each PHP syntax.
Running the Test
Now, switch to the command line (console, terminal) and type the following command:
php /path/to/RudimentaryPhpTest.php --testbase=/path/to/samples/Tutorials/Simple/Test/
Mind to replace the path to point to the location to which you cloned. The parameter testbase gives the path to your test class or a folder that contains it. The framework will then scan the folder for test classes and print the test log to the console (standard output).
After execution of the tests, something similar to the the following should appear:
Running Tutorials_Simple_Text->conversionToNativeStringTest
Assertion succeeded at line 26: Objects are equal in a type-safe check.
Assertion succeeded at line 28: Objects are equal in a type-safe check.
Assertion succeeded at line 30: Objects are equal in a type-safe check.
Class Name Method Name Succeeded Failed
Tutorials_Simple_Text conversionToNativeStringTest 3 0
Unless you change the defaults you will see all matched assertions in green whilst any errors (assertion failures) will show up in read. After all test have been executed, a summary table will be printed.
You would now add further tests for the remaining parts of the implementation which are still due to be written. By now, the general procedure of writing and executing units test should be clear.
Extending the Code
The basic use of unit tests is that they ensure existing code does not break. Therefore do not throw your test classes away once the implementation works. In case you should introduce mistakes later, the tests will point this out so that the error is obvious before release.
Continue by adding another method to the implementation. The method shall test if the wrapped text starts with another text. If it does, the method should return TRUE and FALSE otherwise.
public function startWith($start){
$startWithinWrappedString = mb_substr($this->content, 0, mb_strlen($start));
return $startWithinWrappedString===$start;
}
Then add a test to the test class to ensure startWith does work as intended. The test will just call the method with some samples and assume the implementation is correct when all samples are correctly processed. The test looks like this:
public function startsWithTest(){
$implementationUnderTest = new Tutorials_Simple_Implementation_Text("Hello, I'm there to be tested.");
$this->assertEquals(FALSE, $implementationUnderTest->startWith('Something else'));
$this->assertEquals(FALSE, $implementationUnderTest->startWith('Other thing'));
$this->assertTrue($implementationUnderTest->startWith('Hello'), 'Hello is considered to be the start of Hello, …');
}
Execute the tests again using the same command in the command line as before. This time there will be more output since more tests are available. Also note that assertions can take a text to describe the asserted thing a little better to understand for humans.
Further steps:
- Make changes to the implementation (introduce errors) and see that the tests point out the errors
- Read about setting up bootstrapping (initialization code for the test run)
- Read about configuring to integrate the tests with continuous integration frameworks. Unit tests are only useful if executed regularly.