cakephp - 设置 CakePHP 3 插件测试

标签 cakephp testing configuration phpunit cakephp-3.0

我使用 bin/cake bake plugin PluginName 创建了一个插件。部分原因是它创建了 phpunit.xml.dist,但 bake 不会创建所需的文件夹结构或 tests/bootstrap.php 文件。

问题

当我运行 phpunit 时收到“未执行测试”消息:

$ phpunit
PHPUnit 5.1.3 by Sebastian Bergmann and contributors.

Time: 239 ms, Memory: 4.50Mb

No tests executed!

背景信息

我已经在 tests/TestCase 下的插件文件夹中创建了我的测试。我不认为它们是问题所在,但我会在最后发布它们。

我正在使用默认的 phpunit.xml.dist 文件,并将其用于 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';

单元测试

这存在于 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);
    }

最佳答案

要事第一

基础命名空间应该是Contrast\Test ,这也是 bake 应该添加到您的应用程序中的内容 composer.json文件 autoloadautoload-dev部分,以及烘焙插件 composer.json文件。如果您拒绝烘焙以编辑您的内容 composer.json文件,您应该手动添加自动加载条目

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

并重新转储自动加载器

$ composer dump-autoload

因此您的示例测试的命名空间应该是 Contrast\Test\TestCase .

测试文件也需要以Test 为后缀以便 PHPUnit 识别它们。为了使文件可以自动加载,您应该坚持使用 PSR-4,即文件应与类同名,即您的测试文件应命名为 TextColorTest.php。 , 不是 Color.php .

作为应用程序的一部分进行测试

当将插件作为应用程序的一部分进行测试时,插件测试中不一定需要 Bootstrap 文件(bake 应该,但实际上会生成一个),因为您可以使用应用程序的配置来运行它们,它有一个测试引导文件 ( tests/bootstrap.php ),其中包含您的应用程序引导文件 ( config/bootstrap.php )。

因此,测试将从您的应用程序基本文件夹运行,并且您必须传递插件路径,例如

$ vendor/bin/phpunit plugins/Contrast

或者你会在你的应用程序主 phpunit 配置文件中添加一个额外的插件测试套件,它说 <!-- Add your plugin suites -->

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

这样插件测试将与您的应用测试一起运行。

最后,如果存在适当的引导文件,您还可以从插件目录运行测试。默认的当前看起来像:

<?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';

另见

测试独立开发的插件

以独立方式开发插件时,这是您真正需要设置环境的 Bootstrap 文件的时候,这就是插件所在的位置 composer.json正在使用 bake 生成的文件。默认情况下,后者看起来像

{
    "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"
        }
    }
}

烘焙插件时生成的测试 Bootstrap 文件无法开箱即用,因为它所做的只是尝试加载 config/bootstrap.php您的插件文件,默认情况下甚至不存在。

测试 Bootstrap 文件需要做什么当然取决于插件在做什么,但至少它应该

  • 定义核心使用的基本常量和配置
  • 需要 composer 自动加载器
  • 需要 CakePHP 核心引导文件
  • 并加载/注册您的插件。

为了举例,这里有一个 tests/bootstrap.php当前 cakephp/app 的副本示例应用程序模板,即它基本上配置了一个完整的应用程序环境(应用程序定义为在 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]);

为了能够从测试应用程序文件夹中自动加载类,您必须在 composer.json 中添加相应的自动加载条目。文件(并再次重新转储自动加载器),例如:

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

现在使用 bake 创建了插件,配置了环境并安装了 CakePHP 和 PHPUnit 依赖项,您应该能够像使用应用程序一样运行测试,即

$ vendor/bin/phpunit

关于cakephp - 设置 CakePHP 3 插件测试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35565066/

相关文章:

unit-testing - 在哪里测试验证 - 在单元或功能测试中?

configuration - glassfish 中的 SSL 配置

mysql - 如何测试不同时区的数据库记录?

exception - 如何在 Kotlin 中测试预期的未经检查的异常?

PHP_CodeSniffer - 显示失败的嗅探

configuration - 如何在 SourceTree 中为多个存储库中的 Windows 配置 "Commit text links"功能

php - 用可选年份存储生日(生日)字段的最佳方法是什么

javascript - 将 Javascript 变量传递给 CakePdf

Cakephp如何加载I18nModel夹具进行测试?

CakePHP 未在生产服务器上加载与模型相关的属性