Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit Testing with Fat-Free-Framework

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.

like image 550
itskrude Avatar asked Feb 04 '23 08:02

itskrude


1 Answers

Short answer

  • 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


Long answer

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:

  1. Create a reusable file which defines the routes (e.g. a routes.php file or a INI file with route defintions)

  2. 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).

  3. Write the PHPUnit tests

Example

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(){});
like image 122
Rayne Avatar answered Feb 23 '23 17:02

Rayne