Morphodo.com
  • Home
  • Contact
  • Imprint
  • Partner
  • morphing ideas into solutions - TYPO3, FLOW3,
    Mobile Apps ...

  • FLOW3 Tutorials
  • FLOW3 Testing
  • Functional tests for your FLOW3 package

    Creating functional tests can be really helpful if you would like to test complex use cases which depend on framework components.

    Requirements

    Make sure that the following libraries are installed on your system:

    • pdo_sqlite
    • phpunit

    You can put the unit tests into the folder "Tests" as top level directory in the same package of your FLOW3 application. If you would rather like to have a different location for your unit tests you can also create a separate package which is only responible for your unit tests.

    In our case I decided to store the unit tests in the same package where i develop my application because i feel more comfortable this way.

    Create the "Tests/FunctionalTests.xml" configuration

    <?xml version="1.0"?>
    <phpunit strict="true"
                    bootstrap="FunctionalTestBootstrap.php"
                    convertErrorsToExceptions="true"
                    convertNoticesToExceptions="true"
                    convertWarningsToExceptions="true">
            <testsuites>
                    <testsuite name="All tests">
                            <directory>Functional</directory>
                    </testsuite>
            </testsuites>
    </phpunit>
    
    
    

    All available configuration options can be checked at http://www.phpunit.de.

    Create the bootstrap "Tests/FunctionalTestBootstrap.php"

    <?php
    namespace F3\FLOW3\Build;
    
    @require_once('vfsStream/vfsStream.php');
    if (!class_exists('vfsStreamWrapper')) {
            exit(PHP_EOL . 'FLOW3 Bootstrap Error: The functional test bootstrap requires vfsStream to be installed (e.g. via PEAR). Please also make sure that it is accessible via the PHP include path.' . PHP_EOL . PHP_EOL);
    }
    
    $_SERVER['FLOW3_ROOTPATH'] = dirname(__FILE__) . '/../../../../';
    
    require_once($_SERVER['FLOW3_ROOTPATH'] . 'Packages/Framework/FLOW3/Classes/Core/Bootstrap.php');
    
    $bootstrap = new \F3\FLOW3\Core\Bootstrap('Testing');
    $bootstrap->run();
    

    It is important to set the "FLOW3_ROOTPATH" to the system root of your FLOW3 installation which is normally /var/www/yourProject - don't mix it up with the webroot which is /var/www/yourProject/Web!

    Writing the first functional Unit test

    Before we start to create our test class we should think about the general directory structure we would like to use.

    I recommend to structure the tests in several subdirectories to make it clear what the test is for. Below you can see how the directory structure looks if you write a controller test case.

    Tests
    ├── Functional
    │   └── Controller
    │       └── EventControllerTest.php
    ├── FunctionalTestBootstrap.php
    └── FunctionalTests.xml

    Simulating web requests

    The following test case is written to verify the creation of an event model. You may think writing unit tests for a simple model is oversized, usually you are right with this. However, in this case the parts of the FLOW3 framework where the arguments are transfered to real objects by the PropertyMapper should be tested. This is the point where the test case can be used to make sure the DateTimeConverter was configured as needed. If you do not configure the DateTimeConverter, it is not possible to create custom formatted date values within your event model, except for the case when you would like to use the default date format "YYYY-MM-DDT##:##:##+##:##".

    <?php
    namespace T3DD11\Events\Tests\Functional\Controller;
    
    ...
    
    /**
     * Verify that we are able to create a new Event
     */
    class EventControllerTest extends \TYPO3\FLOW3\Tests\FunctionalTestCase {
    
        /**
         * @var boolean
         */
        static protected $testablePersistenceEnabled = true;
    
        /**
         * @var \T3DD11\Events\Domain\Repository\EventRepository
         */
        protected $eventRepository;
    
        /**
         * Initialize repository
         *
         * @return void
         */
        public function setUp() {
            parent::setUp();
            $this->eventRepository = new \T3DD11\Events\Domain\Repository\EventRepository;
        }
    
        /**
         * Test that we are able to create a new Event using
         * the "createAction" of the Event controller.
         *
         * We expect that the new Event will be created in the
         * database and can be found using the repository.
         *
         * @test
         * @return void
           */
        public function createNewEvent() {
            $arguments = array(
                'event' => array(
                    'title' => 'T3DD11',
                    'date' => '2011-06-30',
                    'description' => 'Some description',
                    'url' => 'http://t3dd11.typo3.org/',
                )
            );
    
                // Simulate web request
            $this->sendWebRequest('Event', 'T3DD11.Events', 'create', $arguments, $format = 'html');
    
                // Persist the new created Event
            $this->persistenceManager->persistAll();
    
            $events = $this->eventRepository->findAll();
    
            $this->assertEquals(1, $events->count(), 'Unexpected amount of available Events found.');
    
            $event = $events->getFirst();
            $this->assertEquals($arguments['event']['title'], $event->getTitle(), 'Event with wrong title.');
        }
    }
    
    

    The first highlighted line "$testablePersistenceEnabled = true;" is important in order to enable database driven tests through a "pdo_sqlite" of the type "memory".

    The method "sendWebRequest()" will simulate a HTTP web request which will take care about the most important parts of the framework.

        
        /**
         * Calls the given action of the given controller
         *
         * @param string $controllerName The name of the controller to be called
         * @param string $controllerPackageKey The package key the controller resides in
         * @param string $controllerActionName The name of the action to be called, e.g. 'index'
         * @param array $arguments Optional arguments passed to controller
         * @param string $format The request format, defaults to 'html'
         * @return string The result of the controller action
         * @author Andreas Förthner <andreas.foerthner@netlogix.de>
         */
        protected function sendWebRequest($controllerName, $controllerPackageKey, $controllerActionName, array $arguments = array(), $format = 'html');
    
    
    

    Running the unit test

    $ phpunit -c FunctionalTests.xml
    PHPUnit 3.5.13 by Sebastian Bergmann.
    
    .
    
    Time: 1 second, Memory: 63.75Mb
    
    OK (1 test, 4 assertions)
    
    
    

    Trouble shooting

    • The tests won't run because of a strange bootstrapping error on the command line:
      • Flush the cache with "./flow3 cache:flush" (must be executed in the system root directory).
    • You can not install "pdo_sqlite" for database driven test cases:
      • Just create a new test database and configure the database settings in the "Confiugration/Testing/Settings.yaml" file.
    • Call to undefined method Mock_ResponseInterface_0c2c646f::setStatus():
      • The action you try to test contains the call "$this->recirect()" which is currently not supported in the testing context. The solution is to remove this line temporary or send me an e-mail if you are looking for a permanent solution.

    Getting the complete code example

    You can play with the example code and figure out how it works in action.

    git clone --recursiv git@gitorious.org:t3dd11/t3dd11.git
    cd t3dd11
    git checkout -b functional-tests T3DD11.Events-Part-5 
    
    
    blog comments powered by Disqus