Configuring CakePHP 3 Plugin Testing

I used bin/cake bake plugin PluginName to create a plugin. Part of this is that it creates phpunit.xml.dist , but bake does not create the required folder structure or tests/bootstrap.php .

Problem

When I run phpunit :

I get the message "No tests"
 $ phpunit PHPUnit 5.1.3 by Sebastian Bergmann and contributors. Time: 239 ms, Memory: 4.50Mb No tests executed! 

Background Information

I created my tests in my plugins folder in tests/TestCase . I do not think this is a problem, but I will post them at the end.

I use the phpunit.xml.dist file by default and I use it for tests/bootstrap.php :

 $findRoot = function ($root) { do { $lastRoot = $root; $root = dirname($root); if (is_dir($root . '/vendor/cakephp/cakephp')) { return $root; } } while ($root !== $lastRoot); throw new Exception("Cannot find the root of the application, unable to run tests"); }; $root = $findRoot(__FILE__); unset($findRoot); chdir($root); define('ROOT', $root); define('APP_DIR', 'App'); define('WEBROOT_DIR', 'webroot'); define('APP', ROOT . '/tests/App/'); define('CONFIG', ROOT . '/tests/config/'); define('WWW_ROOT', ROOT . DS . WEBROOT_DIR . 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 . '/vendor/cakephp/cakephp'); define('CORE_PATH', CAKE_CORE_INCLUDE_PATH . DS); define('CAKE', CORE_PATH . 'src' . DS); require ROOT . '/vendor/autoload.php'; require CORE_PATH . 'config/bootstrap.php'; 

Unit test

This is in tests/TestCase/Color.php

 <?php namespace Contrast\TestCase; use Cake\TestSuite\TestCase; use Contrast\Text\Color; /** * Contrast Text Color tests */ class TextColorTest extends TestCase { /** * @test * @return void */ public function testShouldHandleVarietyOfColors() { # Returns black $this->assertEquals(Color::getBlackWhiteContrast('#FFFFFF'), 'black'); // Why won't you fail?? $this->assertEquals(true, false); } 
+5
source share
1 answer

First things first

The base namespace should be Contrast\Test , which also means that cookies should be added to your composer.json files autoload and autoload-dev sections, as well as to baked composer.json plugin files. If you refuse to bake to edit yout composer.json file, you must add an autoload entry manually

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

and reset the autoloader again

 $ composer dump-autoload 

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

In addition, test files must be added using Test so that PHPUnit can recognize them. And in order for the files to be autoload, you must adhere to PSR-4, i.e. the files must have the same name as the class, i.e. Your test file should be named TextColorTest.php , not Color.php .

Testing as part of the application

When testing the plugin as part of the application, there is no need for the bootstrap file for plugin tests (bake should actually generate one), since you could run them with the configuration of your application, which has a test boot file ( tests/bootstrap.php ), which includes a bootstrap file for your applications ( config/bootstrap.php ).

Therefore, the tests will run from your base application folder and you will need to go through the path to the plugin, for example

 $ vendor/bin/phpunit plugins/Contrast 

or you add an additional plugin testsuite to your main phpunit configuration file, where <!-- Add your plugin suites -->

 <testsuite name="Contrast Test Suite"> <directory>./plugins/Contrast/tests/TestCase</directory> </testsuite> 

In this way, plugin tests will run along with your applications.

Finally, you can also run tests from the plugin directory, given that there is a corresponding bootstrap file. By default, it looks like this:

 <?php /** * Test suite bootstrap for Contrast. * * This function is used to find the location of CakePHP whether CakePHP * has been installed as a dependency of the plugin, or the plugin is itself * installed as a dependency of an application. */ $findRoot = function ($root) { do { $lastRoot = $root; $root = dirname($root); if (is_dir($root . '/vendor/cakephp/cakephp')) { return $root; } } while ($root !== $lastRoot); throw new Exception("Cannot find the root of the application, unable to run tests"); }; $root = $findRoot(__FILE__); unset($findRoot); chdir($root); require $root . '/config/bootstrap.php'; 

see also

Testing stand-alone developed plugins

When developing plugins offline, this is when you really need a boot file that sets up your environment, and this is where the plugins composer.json file generated by bake is used. By default, the latter will 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 created when the plugin was baked does not work out of the box, since everything he would do would try to load your plugin's config/bootstrap.php file, which by default does not even exist.

What a test bootstrap file should do depends on what the plugin does, but at least it should

  • define the basic constants and configuration used by the kernel
  • composer autoloader required
  • CakePHP bootstrap required
  • and upload / register your plugin.

For example, here is an example of tests/bootstrap.php with a copy of what the current cakephp/app application template cakephp/app , that is, it basically sets up the entire application environment (with the application that is defined in the tests/TestApp ),

 // 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 the classes to be autoloadable from the folder of the test application, you need to add the corresponding autoload entry to your composer.json file (and reset the autoloader again), for example:

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

So, now that the plugin has been created using bake, a customized environment and the installed dependencies of CakePHP and PHPUnit, you should be able to run your tests in the same way as with the application, i.e.

 $ vendor/bin/phpunit 
+4
source

Source: https://habr.com/ru/post/1243620/


All Articles