【问题标题】:PHPUnit autoloading classesPHPUnit 自动加载类
【发布时间】:2015-09-05 02:34:39
【问题描述】:

简短的问题

如何让 Autoloader 找到运行 PHP 测试所需的所有类?

问题详细

我想在 Eclipse 中自动加载我在 PHPUnit 中使用的类。我的目录结构如下。

Project (called yii-app)
    protected
        dirA
            classA.php
        dirB
            classB.php
    yii-1.1.14.f0fee9
        Yii.php
    tests
        ClassATest.php
        ClassBTest.php
        bootstrap.php
        Autoloader.php

我使用的是我找到的herebootstrap.phpAutoloader.php,详情见下文。 classA 类没有使用 Yii 框架,ClassATest 中的测试运行顺利。 classB 类确实使用了 Yii 框架。第一行之一是:

Yii::import('application.<path into some directory>.*')

当我尝试在 ClassBTest.php 中运行测试时,出现以下错误。

Fatal error: Class 'Yii' not found in /Users/physicalattraction/git/yii-app/protected/dirB/classB.php on line 3

即使我注册了整个项目目录(包括子目录),也找不到 Class Yii,而它就在那里。我应该更改哪些内容才能使这些测试也运行?

注意

如果我尝试直接从终端运行测试,我也会遇到同样的问题,因此它与 Eclipse 无关。

$ ./composer/vendor/bin/phpunit --bootstrap=tests/bootstrap.php tests
PHPUnit 4.5.1 by Sebastian Bergmann and contributors.

Fatal error: Class 'Yii' not found in /Users/physicalattraction/git/yii-app/protected/dirB/classB.php on line 3

详情

Eclipse 中的 PHPUnit 设置

bootstrap.php

<?php
include_once('AutoLoader.php');
// Register the directory to your include files
Toolbox\Testing\AutoLoader::registerDirectory(__DIR__.'/../yii-1.1.14.f0fee9');
Toolbox\Testing\AutoLoader::registerDirectory(__DIR__.'/../protected');
?>

Autoloader.php

    <?php

namespace Toolbox\Testing;

/**
 * This class is an auto loader for use with vanilla PHP projects' testing environment. Use it in
 * the bootstrap to register classes without having to use a framework (which you can, and should if
 * it's a better solution for you) and without having to use includes everywhere.
 *
 * It assumes that the file path in relation to the namespace follows the PSR-0 standard.
 *
 * IMPORTANT NOTE: When just registering directories, the class has no ability to discern
 * conflicting class names in different namespaces, which means that classes with the same name will
 * override each other! Always use the registerNamespace()-method if possible!
 *
 * Inspired by Jess Telford's AutoLoader (http://jes.st/).
 *
 * @see http://jes.st/2011/phpunit-bootstrap-and-autoloading-classes/
 * @see http://petermoulding.com/php/psr
 * @see http://www.php-fig.org/psr/psr-0/
 *
 * @codeCoverageIgnore
 *
 * @category    Toolbox
 * @package     Testing
 *
 * @author      Helge Söderström <helge.soderstrom@schibsted.se>
 */

class AutoLoader {

    /**
     * An array keeping class names as key and their path as the value for classes registered with
     * AutoLoader::registerNamespace().
     *
     * @var array
     */
    protected static $namespaceClassNames = array();

    /**
     * An array keeping class names as key and their path as the value for classes registered with
     * AutoLoader::registerDirectory().
     *
     * @var array
     */
    protected static $directoryClassNames = array();


    /**
     * Store the filename (sans extension) & full path to all ".php" files found for a namespace.
     * The parameter should contain the root namespace as the key and the directory as a value.
     *
     * @param string $namespace
     * @param string $dirName
     * @return void
     */
    public static function registerNamespace($namespace, $dirName) {
        $directoryContents = new \DirectoryIterator($dirName);
        foreach($directoryContents as $file) {
            if ($file->isDir() && !$file->isLink() && !$file->isDot()) {
                $newNamespace = $namespace . "_" . $file->getFileName();
                $newDirName = $dirName . "/" . $file->getFilename();
                static::registerNamespace($newNamespace, $newDirName);
            } elseif (substr($file->getFilename(), -4) === '.php') {
                $className = substr($file->getFilename(), 0, -4);
                $namespacedClassName = $namespace . "_" . $className;
                $fileName = realpath($dirName) . "/" . $file->getFilename();
                static::$namespaceClassNames[$namespacedClassName] = $fileName;
            }
        }
    }


    /**
     * Store the filename (sans extension) & full path of all ".php" files found.
     *
     * NOTE: This method will not be able to differentiate the same class names in different
     *       namespaces and will therefore overwrite class names if multiple of the same name is
     *       found. If possible, use registerNamespace instead!
     *
     * @param string $dirName
     * @return void
     */
    public static function registerDirectory($dirName) {
        $directoryContents = new \DirectoryIterator($dirName);
        foreach ($directoryContents as $file) {
            if ($file->isDir() && !$file->isLink() && !$file->isDot()) {
                // Recurse into directories other than a few special ones.
                static::registerDirectory($file->getPathname());
            } elseif (substr($file->getFilename(), -4) === '.php') {
                // Save the class name / path of a .php file found.
                $className = substr($file->getFilename(), 0, -4);
                AutoLoader::registerClass($className, $file->getPathname());
            }
        }
    }


    /**
     * Caches a found class with the class name as key and its path as value for use when loading
     * on the fly. The class is registered with its class name only, no namespace.
     *
     * @param string $className
     * @param string $fileName
     * @return void
     */
    public static function registerClass($className, $fileName) {
        AutoLoader::$directoryClassNames[$className] = $fileName;
    }


    /**
     * Includes a found class in the runtime environment. Strips namespaces.
     *
     * @param string $className
     * @return void
     */
    public static function loadClass($className) {
        // First, see if we've registered the entire namespace.
        $namespacedClassName = str_replace('\\', '_', $className);
        if (isset(static::$namespaceClassNames[$namespacedClassName])) {
            require_once(static::$namespaceClassNames[$namespacedClassName]);
            return;
        }

        // Nope. Have we registered it as a directory?
        $psrDirectorySeparators = array('\\', '_');
        foreach($psrDirectorySeparators as $separator) {
            $separatorOccurrence = strrpos($className, $separator);
            if($separatorOccurrence !== false) {
                $className = substr($className, $separatorOccurrence + 1);
                break;
            }
        }

        if (isset(AutoLoader::$directoryClassNames[$className])) {
            require_once(AutoLoader::$directoryClassNames[$className]);
        }
    }

}

// Register our AutoLoad class as the system auto loader.
spl_autoload_register(array('Toolbox\Testing\AutoLoader', 'loadClass'));
?>

【问题讨论】:

    标签: php yii phpunit autoload


    【解决方案1】:

    Autoloader 可能找不到 YII 类。您是否尝试添加:

    Toolbox\Testing\AutoLoader::registerDirectory(DIR.'/../yii-1.1.14.f0fee9/framework');

    到您的 bootstrap.php 文件。我认为YII类是在framework目录下定义的。

    您可以尝试的另一件事是改用 composer 自动加载器。

    附言 在测试目录中镜像您的应用程序目录/文件结构是一个很好的做法。在您的情况下,ClassATest.php 和 ClassBTest.php 应该被分隔到它们自己的目录中,就像它们在受保护目录中的分隔方式一样。

    【讨论】:

    • 在我将文件 yii.php 重命名为 Yii.php 之后,自动加载器确实找到了 Yii 类。即使在尝试了 Yii + 3rd 方库自动加载器部分的建议后,问题仍然存在:yiiframework.com/doc/guide/1.1/nl/…。感谢您对测试目录结构的实践。我还没有想到,但确实有道理。
    【解决方案2】:

    我发现通过以下更改,它起作用了。

    有如下目录结构

    project
        protected
            config
                main.php
                test.php
            controllers
            models
            tests
                fixtures
                functional
                report
                unit
                bootstrap.php
                phpunit.xml
    

    main.php: 添加 Yii 查找类的目录。 Yii 不会自动搜索子目录,因此您必须在此处单独指定每个目录。

    // autoloading model and component classes
    'import' => array(
        'application.models.*',
        'application.models.support.*',
        'application.components.*',
        ....
    

    test.php中定义如下测试配置。

    <?php
    return CMap::mergeArray(
        require(dirname(__FILE__).'/main.php'),
        array(
            'components'=>array(
                'fixture'=>array(
                    'class'=>'system.test.CDbFixtureManager',
                ),
                'db'=>array(
                    'class' => 'CDbConnection',
                    'connectionString' => 'CONNECTIONSTRING',
                    'emulatePrepare' => true,
                    'username' => 'USERNAME',
                    'password' => 'PASSWORD',
                    'charset' => 'utf8',
                ),
            ),
        )
    );
    ?>
    

    然后,在引导文件中,只使用 Yii Autoloader。

    <?php
    $yiit=__DIR__.'/../../yii-1.1.14.f0fee9/yiit.php';
    $config=dirname(__FILE__).'/../config/test.php';
    require_once($yiit);
    // Include the following line if you want to write a WebTestCase
    // require_once(dirname(__FILE__).'/WebTestCase.php');
    Yii::createWebApplication($config);
    ?>
    

    我的测试用例需要一个 Order 类型的模型,它连接到一个数据库表 orders。在目录fixtures/Order.php 中定义一个fixture。

    <?php    
    return array(
        'order1' => array(
            'id' => 1011,
            'order_number' => 'on_101'
        )
    );
    ?>
    

    按照 Yii 和 PHPUnit 网站上的说明制作测试用例。

    class MyExportTest extends CDbTestCase
    {
        public $fixtures = array(
            'orders' => 'Order'
        );
    
        public function test_order()
        {
            $order = $this->orders('order1');
            $this->assertTrue($order instanceof Order);
            $r = $order->order_number;
            $e = "on_101";
            $this->assertEquals($r, $e,
            sprintf("Received order_number: \n%s\nExpected order_number: \n%s\n", $r, $e));
        }
    
        public function test_export()
        {
            $sut = new MyExport($tenantId, $userGroupId);
            $r = $sut->method_to_test()
            $e = "expected result";
            $this->assertEquals($r, $e,
            sprintf("Received result: \n%s\nExpected result: \n%s\n", $r, $e));
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2020-04-13
      • 1970-01-01
      • 1970-01-01
      • 2018-04-29
      • 2014-10-02
      • 2017-06-13
      • 2021-06-18
      • 2016-02-17
      相关资源
      最近更新 更多