Unit testing using no fat
Is there a way to use PHPUnit where I have a test folder with indexTest.php inside that checks the routes in my index.php file?
The fat-free guide gives code snippets for mocking route and POSTS requests. I was able to get such a test to work if I generate the route directly in my test file with any functionality in it.
What I would like is to mock the route with tokens, let it run from the route in index.php and through the controller, and test the 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 would rather not declare the whole route this way, maybe I am completely wrong and this is not possible? The above code is running a running provider / bin / phpunit. Relative examples and tutorials are hard to find on this one.
source to share
Short answer
-
Separate your controller code from bootstrap and routing code
-
Reuse routing configuration in your environments eg. website, CLI and test environment
-
Use
Base->mock()
in your tests to mock previously defined routes -
Do not run
Base->run()
in a testing environment
Long answer
I plan to write a long article about testing F3 routes, but due to lack of time, I will just give a few points here:
-
Create a reusable file that defines routes (such as a file
routes.php
or a fileINI
with a route) -
Load the routes before running the test code. This can be easily done using your own bootstrap file for PHPUnit (
--bootstrap <FILE>
or use the appropriate directive in your PHPUnit configuration). -
Write PHPUnit tests
Example
The following example is an adaptation of my GitHub Gist :
bootstrapping-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 a custom boot file for PHPUnit -
bootstrap-website.php
is the download file for the website -
bootstrap-shared.php
contains information common to all environments. The file can contain routing information. I have stripped the routing information in an example:routes.php
-
ExampleTest.php
is a regular PHPUnit test -
The snippet
$f3->set('QUIET', true);
must be added to the custom bootstrap file. It is also a good idea to introduce a variable to indicate that the application is running in test mode, for example$f3->set('APP.TEST', true)
-
F3 doesn't clear your variables between tests / mocks. You can save the original state before running the tests and then restore the state in the PHPUnit method
setUp()
-
Instead of rendering the pages, it may also be sufficient to collect only the data that needs to be available for rendering. In this case, use the injected variable
APP.TEST
in your view to skip rendering
Notes for future update answers
-
ini_set('error_log','./phpunit/error.log')
-
$f3->set('ONERROR',function(){});
source to share