Yii unit testing: phpunit + bootstrap extension + getBasePath

I have a Yii project using the latest bootstrap extension (which might not be important), and while trying to run unit tests, it throwed me an error:


PHP Fatal error:  Uncaught exception 'CException' with message 'CAssetManager.basePath "/usr/bin/assets" is invalid. Please make sure the directory exists and is writable by the Web server process.' in /Users/belerophon/Documents/blah/repos/blah2/libs/yii/yii-1.1.11.58da45/framework/web/CAssetManager.php:134

Stack trace:

#0 /Users/belerophon/Documents/blah/repos/blah2/libs/yii/yii-1.1.11.58da45/framework/web/CAssetManager.php(119): CAssetManager->setBasePath('/usr/bin/assets')

#1 /Users/belerophon/Documents/blah/repos/blah2/libs/yii/yii-1.1.11.58da45/framework/web/CAssetManager.php(240): CAssetManager->getBasePath()

#2 /Users/belerophon/Documents/blah/repos/blah2/libs/bootstrap/yii-bootstrap-1.0.0.alpha5.r280/components/Bootstrap.php(328): CAssetManager->publish('/Users/beleroph...', false, -1, false)

#3 /Users/belerophon/Documents/blah/repos/blah2/libs/bootstrap/yii-bootstrap-1.0.0.alpha5.r280/components/Bootstra in /Users/belerophon/Documents/blah/repos/blah2/libs/yii/yii-1.1.11.58da45/framework/web/CAssetManager.php on line 134

I don’t understand [color="#FF0000"]why getBasePath is returning “/usr/bin”[/color], when running the test code.

Btw, I ran it like this:




cd protected/tests

phpunit unit/UserTest.php



I don’t know if this is worth mentioning, but in my directory structure I’m using some symlinks. I have this:




|-- libs

|   |-- bootstrap

|   |   |-- latest -> yii-bootstrap-1.0.0.alpha5.r280

|   |   |-- yii-bootstrap-0.10.1.beta.r254

|   |   `-- yii-bootstrap-1.0.0.alpha5.r280

|   |-- yii

|   |   |-- latest -> yii-1.1.11.58da45

|   |   |-- yii-1.1.10.r3566

|   |   `-- yii-1.1.11.58da45

`-- src

    `-- project.com

        |-- images

        |-- index-test.php

        |-- index.php

        |-- protected

        |   |-- tests

        |   |   |-- WebTestCase.php

        |   |   |-- bootstrap.php

        |   |   |-- fixtures

        |   |   |-- functional

        |   |   |-- phpunit.xml

        |   |   `-- unit

        |   |-- yiic

        |   |-- yiic.bat

        |   `-- yiic.php

        |-- temp.html

        `-- themes

            `-- classic

                `-- views



Notice the symlinks on the lib’s folders.

So, for example, my index.php looks like this:




<?php


// change the following paths if necessary

$yii=dirname(__FILE__).'/../../libs/yii/latest/framework/yii.php';

$config=dirname(__FILE__).'/protected/config/main.php';


// remove the following lines when in production mode

defined('YII_DEBUG') or define('YII_DEBUG',true);


// specify how many levels of call stack should be shown in each log message

defined('YII_TRACE_LEVEL') or define('YII_TRACE_LEVEL',3);


require_once($yii);

Yii::createWebApplication($config)->run();



I’m working with the latest Yii release, in a Mac OSX Lion, php 5.3.10

For now, I’m simply unable to run tests with phpunit.

Can someone help me here?

Thanks in advance!

André

Oh, and I’m able to run tests as long as I comment the bootstrap entries on main.php configuration file.

But still, I’d like to know what is causing this.

Hey, I tried to digg a little and my conclusions are these:

  • Yii uses $_SERVER[‘SCRIPT_FILENAME’] to determine the entry script

  • what happens when you run unit tests, is that $_SERVER[‘SCRIPT_FILENAME’] gets set to the the phpunit file (on most cases, probably located on /usr/bin/phpunit);

[color="#FF0000"]

From what I understand, this might cause problems when you have your application configured with components that try to publish assets during the application startup.[/color]

So, I imagined that the "entry script", when running tests, [color="#FF0000"]should be index-test.php file[/color]. For this reason, I changed my bootstrap file:




<?php

$_SERVER['SCRIPT_FILENAME'] = dirname(__FILE__) . '/../../index-test.php';

$_SERVER['SCRIPT_NAME'] = basename($_SERVER['SCRIPT_FILENAME']);


// change the following paths if necessary

$yiit=dirname(__FILE__).'/../../../../libs/yii/latest/framework/yiit.php';

$config=dirname(__FILE__).'/../config/test.php';


require_once($yiit);

require_once(dirname(__FILE__).'/WebTestCase.php');


Yii::createWebApplication($config);



I reached these conclusions looking at:

  • getScriptUrl on CHttpRequest.php

  • getBasePath on CAssetManager.php

[color="#FF0000"]Can someone help or comment on these findings?[/color]

I’m quite new to Yii and have little experience with PHP, so really need some help here.

Thanks

André

Bump

Anyone?

What you did looks ok. Bootstrap (in my opinion) is not well designed as it requires to always load the extension, and initialising code publishes assets and adds clientScript entries (as I remeber…). This is very annoying when you try testing such application because CLI scripts do not have those $_SERVER entries (which is obvious because thay are not run in any server context).

What you did is a common workaround providing proper $_SERVER entries in CLI mode, and if it works - I think you should just use it.

Ok, thanks for the insights!

I’ll continue to use the workaround… just wanted to have some opinions on the matter, 'cause I could be missing something here.

Thanks!

this solution did not work for me, the one that worked was to not load bootstrap when running a console application. So change the main config file like this:




	'preload'=>array(

		'log',

		 php_sapi_name() !== 'cli' ?'bootstrap': '',

	),



Thank you josez, that worked for me! I had no idea it was because of yii-bootstrap.

Add this at the beginning of your bootstrap.php file inside tests directory. One should not touch the config.php file.


$_SERVER['SCRIPT_FILENAME'] = realpath(__DIR__.'/../../index.php');

$_SERVER['SCRIPT_NAME'] = basename($_SERVER['SCRIPT_FILENAME']);

This worked for me. Thanks!

You may also add in bootstrap:


if( Yii::app()->hasComponent( 'urlManager' ) ) {

    Yii::app()->getComponent( 'urlManager' )->baseUrl = '';

}