Is there a way to use PHPUnit where I have a test folder with indexTest.php inside that tests routes in my index.php file?
The fat-free guide gives snippets of code for mocking route requests and POSTS. I have only managed to get such a test to work if I generate the route directly in my test file with whatever functionality in it.
What I would like is to mock a route with tokens, allow it to run from a route in index.php and through the controller and test f3 variables that should be set by running the route.
<?php
class indexTest extends \PHPUnit_Framework_TestCase
{
public function test()
{
$f3 = Base::instance();
// Don't write to STDOUT
$f3->set('QUIET', true);
$f3->route('GET /path', function(){ echo 'TEXT'; });
$this->assertNull($f3->mock('GET /path'));
$this->assertSame('TEXT', $f3->get('RESPONSE'));
$f3->route('GET /verify/@answer/@value',
function($f3, $params){
$errors = array();
$answer = $params['answer'];
$value = $params['value'];
$prefix = substr($answer, 0, 3); //pre, ans, pos
$id = (int)substr($answer, 3); //question id number (1, 2, 3, 4)
//$value is the input value from user
$result = check_id($prefix, $id, $value);
if($result !== true){
$errors[] = $result;
}
$f3->set('errors', $errors);
return $errors;
});
function check_id($prefix, $id, $value)
{
if($prefix == 'pre' || $prefix == 'pos'){
if($value <= 0 || $value > 180 || $value === NULL){
echo 'The input value of ' . $prefix . $id . ' question was out of bounds';
return 'The input value of ' . $prefix . $id . ' question was out of bounds';
}else{
return true;
}
}else if($prefix == 'ans'){
if($value < 0 || $value > 10 || $value === NULL){
echo 'The value of quiz ans' + $id + ' was out of bounds';
return 'The value of quiz ans' + $id + ' was out of bounds';
}else{
return true;
}
}else {
return 'The prefix does not match';
}
}
$this->assertNotNull($f3->mock('GET /verify/ans1/8'));
$this->assertEmpty($f3->get('RESPONSE')[0]);
$this->assertNotNull($f3->mock('GET /verify/dsk4/6'));
$this->assertSame('6', $f3->get('PARAMS.value'));
$this->assertSame('dsk4', $f3->get('PARAMS.answer'));
$this->assertEmpty($f3->get('RESPONSE')[0]);
$this->assertNotNull($f3->mock('GET /verify/pre4/250'));
$this->assertSame('The input value of pre4 question was out of bounds', $f3->get('errors')[0]);
$this->assertNotSame('pre4', $f3->get('PARAMS.answer'));
$f3->set('QUIET',FALSE); // allow test results to be shown later
$f3->clear('ERROR'); // clear any errors
}
}
I'd prefer not to declare the entire route like this, maybe I am entirely wrong and this is not possible? The above code works running vendor/bin/phpunit. Relative examples and tutorials are difficult to find on this.
Separate your controller code from bootstrapping and routing code
Reuse the routing configuration in your environments, e.g. website, CLI and testing environment
Use Base->mock()
in your tests to mock the previously defined routes
Don't execute Base->run()
in the testing environment
I'm planning for a long time to write an article about testing F3 routes but due the lack of time I will just give some points here instead:
Create a reusable file which defines the routes (e.g. a routes.php
file or a INI
file with route defintions)
Load the routes before running test code. This could be easily done with a custom bootstrap file for PHPUnit (--bootstrap <FILE>
or use the according directive in PHPUnit's configuration).
Write the PHPUnit tests
The following example is an adaption of my GitHub Gist:
bootstrap-website.php
<?php
$f3 = Base::instance();
require 'bootstrap-shared.php';
// [Custom rules only for the website here]
require 'routes.php';
$f3->run();
bootstrap-test.php
<?php
$f3 = Base::instance();
require 'bootstrap-shared.php';
// [Custom rules only for testing environment here]
$f3->set('QUIET', true);
$f3->set('APP.TEST', true);
require 'routes.php';
routes.php
<?php
/**
* @var $f3 Base
*/
$f3->route('GET /path', function(){ echo 'TEXT'; });
ExampleTest.php
class ExampleTest extends PHPUnit_Framework_TestCase {
public function test() {
// Could also be provided by a custom base TestCase.
$f3 = Base::instance();
$this->assertNull($f3->mock('GET /path'));
$this->assertSame('TEXT', $f3->get('RESPONSE'));
}
}
Some notes:
bootstrap-test.php
is the custom bootstrapping file for PHPUnit
bootstrap-website.php
is the bootstrapping file for the website
bootstrap-shared.php
contains information shared by all environments. The file could include routing information. I separated the routing information in the example: routes.php
ExampleTest.php
is a regular PHPUnit test
The $f3->set('QUIET', true);
snippet should be added to the custom bootstrap file. It's also a good idea to introduce a variable showing that the application is running in a test mode, for instance $f3->set('APP.TEST', true)
F3 doesn't clean up your variables between tests/mocks. You could store the original state before running tests and then restore the state in PHPUnit's setUp()
method
Instead of rendering pages it could also be sufficient to collect only the data which should be available for rendering. In this case use the introduced APP.TEST
variable in your view to skip rendering
Notes for later answer updates
ini_set('error_log','./phpunit/error.log')
$f3->set('ONERROR',function(){});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With