简短问题
如何让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
我使用bootstrap.php
和Autoloader.php
找到here,详见下文。类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
即使我注册了整个项目目录(包括子目录),也找不到类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'));
?>
答案 0 :(得分:1)
Autoloader可能找不到YII类。您尝试添加:
Toolbox\Testing\AutoLoader::registerDirectory(DIR.'/../yii-1.1.14.f0fee9/framework');
到你的bootstrap.php文件。我认为YII类是在框架目录中定义的。
您可以尝试的另一件事是使用composer自动加载器。
P.S 最好在tests目录中镜像您的app目录/文件结构。在你的情况下,ClassATest.php和ClassBTest.php应该被分成它们自己的目录,就像它们在受保护目录中分开一样。
答案 1 :(得分:0)
我发现通过以下更改,它确实有效。
具有以下目录结构
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',
),
),
)
);
?>
然后,在bootstrap文件中,仅使用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
中定义一个夹具。
<?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));
}
}