简短的问题
如何使Autoloader查找运行PHP测试所需的所有类?
How can I make the Autoloader find all classes required to run my PHP tests?
详细问题
我想自动加载我在Eclipse的PHPUnit中使用的类.我的目录结构如下.
I want to autoload the classes that I am using in PHPUnit in Eclipse. My directory structure is as follows.
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我使用在此处找到的bootstrap.php和Autoloader.php,请参见下文以了解详细信息.类classA不使用Yii框架,并且ClassATest中的测试运行平稳.类classB确实使用了Yii框架.第一行是:
I use the bootstrap.php and Autoloader.php that I found here, see below for details. The Class classA does not make use of the Yii framework, and the tests in ClassATest run smoothly. The Class classB does make use of the Yii framework. One of the first lines is:
Yii::import('application.<path into some directory>.*')当我尝试在ClassBTest.php中运行测试时,出现以下错误.
When I try to run the tests in ClassBTest.php, I get the following error.
Fatal error: Class 'Yii' not found in /Users/physicalattraction/git/yii-app/protected/dirB/classB.php on line 3即使我注册了整个项目目录(包括子目录),也没有找到类Yii,尽管它在那里.为了使这些测试也能运行,我应该改变什么?
Even if I register the entire project directory (including subdirectories), Class Yii is not found, while it is right there. What should I change to make these tests run as well?
注意
如果我尝试直接从终端运行测试,则会遇到相同的问题,因此它与Eclipse不相关.
I have the same problem if I try to run the tests directly from the terminal, so it is not Eclipse related.
$ ./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详细信息
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 (jes.st/). * * @see jes.st/2011/phpunit-bootstrap-and-autoloading-classes/ * @see petermoulding/php/psr * @see www.php-fig/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')); ?>推荐答案
我发现通过以下更改,它可以正常工作.
I found that with the following changes, it worked.
具有以下目录结构
project protected config main.php test.php controllers models tests fixtures functional report unit bootstrap.php phpunit.xml在main.php中:添加Yii在其中查找类的目录. Yii不会自动搜索子目录,因此您必须在此处分别指定每个目录.
In main.php: add the directories in which Yii looks for classes. Subdirectories are not automatically searched for by Yii, so you have to specify each directory individually here.
// autoloading model and component classes 'import' => array( 'application.models.*', 'application.models.support.*', 'applicationponents.*', ....在test.php中定义以下测试配置.
Define the following test configuration in 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自动加载器.
Then, in the bootstrap file, only use the 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中定义一个灯具.
My test case needs a model of type Order, which is connected to a database table orders. Define a fixture in directory fixtures/Order.php.
<?php return array( 'order1' => array( 'id' => 1011, 'order_number' => 'on_101' ) ); ?>按照Yii和PHPUnit网站上的指示进行测试.
Make the test case as indicated on the Yii and PHPUnit websites.
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)); } }更多推荐
PHPUnit自动加载类
发布评论