Setting up CakePHP 3 Plugin testing

前端 未结 1 813
名媛妹妹
名媛妹妹 2021-02-03 14:10

I\'ve used bin/cake bake plugin PluginName to create a plugin. Part of it is that it creates phpunit.xml.dist, but bake doesn\'t create the folder stru

1条回答
  •  礼貌的吻别
    2021-02-03 14:47

    First things first

    The base namespace should be Contrast\Test, which is also what bake should have added to your applications composer.json files autoload and autoload-dev sections, as well as to the baked plugins composer.json file. In case you denied bake to edit yout composer.json file, you should add the autoload entry manually

    "autoload": {
        "psr-4": {
            // ...
            "Contrast\\": "./plugins/Contrast/src"
        }
    },
    "autoload-dev": {
        "psr-4": {
            // ...
            "Contrast\\Test\\": "./plugins/Contrast/tests"
        }
    },
    

    and re-dump the autoloader

    $ composer dump-autoload
    

    So the namespace for your example test should be Contrast\Test\TestCase.

    Also test files need to be postfixed with Test in order for PHPUnit to recognize them. And in order for the files to be autoloadable, you should stick to PSR-4, ie the files should have the same name as the class, ie your test file should be named TextColorTest.php, not Color.php.

    Testing as a part of an application

    When testing a plugin as a part on an application, there is not necessarily a need for a bootstrap file in the plugin tests (bake should, actually does generate one though), as you could run them with the configuration of your application, which has a test bootstrap file (tests/bootstrap.php) that includes your applications bootstrap file (config/bootstrap.php).

    Consequently the tests would be run from your applications base folder, and you'd have to pass the plugin path, like

    $ vendor/bin/phpunit plugins/Contrast
    

    or you'd add an additional plugin testsuite in your apps main phpunit configuration file, where it says

    
        ./plugins/Contrast/tests/TestCase
    
    

    That way the plugin tests will be run together with your app tests.

    Finally, you can also run the tests from the plugin directory, given that a proper bootstrap file exists. The default one currently looks like:

    See also

    • Cookbook > Testing > Running Tests > Combining Test Suites for Plugins

    • Cookbook > Testing > Creating Tests for Plugins

    Testing standalone developed plugins

    When developing plugins in a standalone fashion, this is when you really need a bootstrap file that sets up the environment, and this is where the plugins composer.json file generated by bake is being used. By default the latter would look like

    {
        "name": "your-name-here/Contrast",
        "description": "Contrast plugin for CakePHP",
        "type": "cakephp-plugin",
        "require": {
            "php": ">=5.4.16",
            "cakephp/cakephp": "~3.0"
        },
        "require-dev": {
            "phpunit/phpunit": "*"
        },
        "autoload": {
            "psr-4": {
                "Contrast\\": "src"
            }
        },
        "autoload-dev": {
            "psr-4": {
                "Contrast\\Test\\": "tests",
                "Cake\\Test\\": "./vendor/cakephp/cakephp/tests"
            }
        }
    }
    

    The test bootstrap file generated when baking a plugin doesn't work out of the box, as all it would do would be trying to load the config/bootstrap.php file of your plugin, which by default doesn't even exist.

    What the test bootstrap file needs to do depends on what the plugin is doing of course, but at the very least it should

    • define the basic constants and configuration that is used by the core
    • require the composer autoloader
    • require the CakePHP core bootstrap file
    • and load/register your plugin.

    For example purposes, here's a tests/bootstrap.php example with a copy of what the current cakephp/app application template does, ie it basically configures a full application environment (with the application defined to be found in the tests/TestApp folder):

    // from `config/paths.php`
    
    if (!defined('DS')) {
        define('DS', DIRECTORY_SEPARATOR);
    }
    define('ROOT', dirname(__DIR__));
    define('APP_DIR', 'test_app');
    define('APP', ROOT . DS . 'tests' . DS . APP_DIR . DS);
    define('CONFIG', ROOT . DS . 'config' . DS);
    define('WWW_ROOT', APP . 'webroot' . DS);
    define('TESTS', ROOT . DS . 'tests' . DS);
    define('TMP', ROOT . DS . 'tmp' . DS);
    define('LOGS', TMP . 'logs' . DS);
    define('CACHE', TMP . 'cache' . DS);
    define('CAKE_CORE_INCLUDE_PATH', ROOT . DS . 'vendor' . DS . 'cakephp' . DS . 'cakephp');
    define('CORE_PATH', CAKE_CORE_INCLUDE_PATH . DS);
    define('CAKE', CORE_PATH . 'src' . DS);
    
    
    // from `config/app.default.php` and `config/bootstrap.php`
    
    use Cake\Cache\Cache;
    use Cake\Console\ConsoleErrorHandler;
    use Cake\Core\App;
    use Cake\Core\Configure;
    use Cake\Core\Configure\Engine\PhpConfig;
    use Cake\Core\Plugin;
    use Cake\Database\Type;
    use Cake\Datasource\ConnectionManager;
    use Cake\Error\ErrorHandler;
    use Cake\Log\Log;
    use Cake\Mailer\Email;
    use Cake\Network\Request;
    use Cake\Routing\DispatcherFactory;
    use Cake\Utility\Inflector;
    use Cake\Utility\Security;
    
    require ROOT . DS . 'vendor' . DS . 'autoload.php';
    require CORE_PATH . 'config' . DS . 'bootstrap.php';
    
    $config = [
        'debug' => true,
    
        'App' => [
            'namespace' => 'App',
            'encoding' => env('APP_ENCODING', 'UTF-8'),
            'defaultLocale' => env('APP_DEFAULT_LOCALE', 'en_US'),
            'base' => false,
            'dir' => 'src',
            'webroot' => 'webroot',
            'wwwRoot' => WWW_ROOT,
            'fullBaseUrl' => false,
            'imageBaseUrl' => 'img/',
            'cssBaseUrl' => 'css/',
            'jsBaseUrl' => 'js/',
            'paths' => [
                'plugins' => [ROOT . DS . 'plugins' . DS],
                'templates' => [APP . 'Template' . DS],
                'locales' => [APP . 'Locale' . DS],
            ],
        ],
    
        'Asset' => [
            // 'timestamp' => true,
        ],
    
        'Security' => [
            'salt' => env('SECURITY_SALT', '__SALT__'),
        ],
    
        'Cache' => [
            'default' => [
                'className' => 'File',
                'path' => CACHE,
                'url' => env('CACHE_DEFAULT_URL', null),
            ],
    
            '_cake_core_' => [
                'className' => 'File',
                'prefix' => 'myapp_cake_core_',
                'path' => CACHE . 'persistent/',
                'serialize' => true,
                'duration' => '+2 minutes',
                'url' => env('CACHE_CAKECORE_URL', null),
            ],
    
            '_cake_model_' => [
                'className' => 'File',
                'prefix' => 'myapp_cake_model_',
                'path' => CACHE . 'models/',
                'serialize' => true,
                'duration' => '+2 minutes',
                'url' => env('CACHE_CAKEMODEL_URL', null),
            ],
        ],
    
        'Error' => [
            'errorLevel' => E_ALL & ~E_DEPRECATED,
            'exceptionRenderer' => 'Cake\Error\ExceptionRenderer',
            'skipLog' => [],
            'log' => true,
            'trace' => true,
        ],
    
        'EmailTransport' => [
            'default' => [
                'className' => 'Mail',
                // The following keys are used in SMTP transports
                'host' => 'localhost',
                'port' => 25,
                'timeout' => 30,
                'username' => 'user',
                'password' => 'secret',
                'client' => null,
                'tls' => null,
                'url' => env('EMAIL_TRANSPORT_DEFAULT_URL', null),
            ],
        ],
    
        'Email' => [
            'default' => [
                'transport' => 'default',
                'from' => 'you@localhost',
                //'charset' => 'utf-8',
                //'headerCharset' => 'utf-8',
            ],
        ],
    
        'Datasources' => [
            'test' => [
                'className' => 'Cake\Database\Connection',
                'driver' => 'Cake\Database\Driver\Mysql',
                'persistent' => false,
                'host' => 'localhost',
                //'port' => 'non_standard_port_number',
                'username' => 'my_app',
                'password' => 'secret',
                'database' => 'test_myapp',
                'encoding' => 'utf8',
                'timezone' => 'UTC',
                'cacheMetadata' => true,
                'quoteIdentifiers' => false,
                'log' => false,
                //'init' => ['SET GLOBAL innodb_stats_on_metadata = 0'],
                'url' => env('DATABASE_TEST_URL', null),
            ],
        ],
    
        'Log' => [
            'debug' => [
                'className' => 'Cake\Log\Engine\FileLog',
                'path' => LOGS,
                'file' => 'debug',
                'levels' => ['notice', 'info', 'debug'],
                'url' => env('LOG_DEBUG_URL', null),
            ],
            'error' => [
                'className' => 'Cake\Log\Engine\FileLog',
                'path' => LOGS,
                'file' => 'error',
                'levels' => ['warning', 'error', 'critical', 'alert', 'emergency'],
                'url' => env('LOG_ERROR_URL', null),
            ],
        ],
    
        'Session' => [
            'defaults' => 'php',
        ],
    ];
    Configure::write($config);
    
    date_default_timezone_set('UTC');
    mb_internal_encoding(Configure::read('App.encoding'));
    ini_set('intl.default_locale', Configure::read('App.defaultLocale'));
    
    Cache::config(Configure::consume('Cache'));
    ConnectionManager::config(Configure::consume('Datasources'));
    Email::configTransport(Configure::consume('EmailTransport'));
    Email::config(Configure::consume('Email'));
    Log::config(Configure::consume('Log'));
    Security::salt(Configure::consume('Security.salt'));
    
    DispatcherFactory::add('Asset');
    DispatcherFactory::add('Routing');
    DispatcherFactory::add('ControllerFactory');
    
    Type::build('time')
        ->useImmutable()
        ->useLocaleParser();
    Type::build('date')
        ->useImmutable()
        ->useLocaleParser();
    Type::build('datetime')
        ->useImmutable()
        ->useLocaleParser();
    
    
    // finally load/register the plugin using a custom path
    
    Plugin::load('Contrast', ['path' => ROOT]);
    

    In order for classes to be autoloadable from the test application folder, you'd have to add a corresponding autoload entry in your composer.json file (and again re-dump the autoloader), like:

        "autoload-dev": {
            "psr-4": {
                "Contrast\\Test\\": "tests",
                "Contrast\\TestApp\\": "tests/test_app/src", // < here we go
                "Cake\\Test\\": "./vendor/cakephp/cakephp/tests"
            }
        }
    

    So now with the plugin been created using bake, the environment configured and the CakePHP and PHPUnit dependencies installed, you should be able to run your tests as you would with an application, ie

    $ vendor/bin/phpunit
    

    0 讨论(0)
提交回复
热议问题