Chapter 6: IssueTest

Hello everybody,

I have bee reading the book slowly, I am enjoying it a lot I have to say. I am at the end of chapter 6 now, and for some reason the testGetTypeText and testGetStatusText throw the following exception when I run phpunit on IssueTest.php:


Exception: Unknown method 'issues' for class 'IssueTest'.

I have no clue why this doesn’t work, here are the two files: IssueTest.php and Issue.php

IssueTest.php:




<?php

class IssueTest extends CDbTestCase

{

	public $fixtures=array(

		'projects' => 'Project',

		'issues' => 'Issue',

	);

	

	public function testGetTypes()

	{

		$options = Issue::model()->typeOptions;

		$this->assertTrue(is_array($options));

		$this->assertTrue(3 == count($options));

		$this->assertTrue(in_array('Bug', $options));

		$this->assertTrue(in_array('Feature', $options));

		$this->assertTrue(in_array('Task', $options));

	}

	

	public function testGetStatus()

	{

		$statuses = Issue::model()->statusOptions;

		$this->assertTrue(is_array($statuses));

		$this->assertTrue(3 == count($statuses));

		$this->assertTrue(in_array('Not Yet Started', $statuses));

		$this->assertTrue(in_array('Started', $statuses));

		$this->assertTrue(in_array('Finished', $statuses));

	}

	

	public function testGetStatusText()

	{

		$this->assertTrue('Started' == $this->issues('issueBug')->getStatusText());

	}

	

	public function testGetTypeText()

	{

		$this->assertTrue('Bug' == $this->issues('issueBug')->getTypeText());

	}

}



Issue.php:




<?php


/**

 * This is the model class for table "tbl_issue".

 *

 * The followings are the available columns in table 'tbl_issue':

 * @property integer $id

 * @property string $name

 * @property string $description

 * @property integer $project_id

 * @property integer $type_id

 * @property integer $status_id

 * @property integer $owner_id

 * @property integer $requester_id

 * @property string $create_time

 * @property integer $create_user_id

 * @property string $update_time

 * @property integer $update_user_id

 *

 * The followings are the available model relations:

 * @property User $requester

 * @property User $owner

 * @property Project $project

 */

class Issue extends CActiveRecord

{

	// Types constants

	const TYPE_BUG = 0;

	const TYPE_FEATURE = 1;

	const TYPE_TASK = 2;

	

	// Statuses constants

	const STATUS_NOT_YET_STARTED = 0;

	const STATUS_STARTED = 1;

	const STATUS_FINISHED = 2;

	

	/**

	 * Returns the static model of the specified AR class.

	 * @return Issue the static model class

	 */

	public static function model($className=__CLASS__)

	{

		return parent::model($className);

	}


	/**

	 * @return string the associated database table name

	 */

	public function tableName()

	{

		return 'tbl_issue';

	}


	/**

	 * @return array validation rules for model attributes.

	 */

	public function rules()

	{

		// NOTE: you should only define rules for those attributes that

		// will receive user inputs.

		return array(

			array('name', 'required'),

			array('project_id, type_id, status_id, owner_id, requester_id, create_user_id, update_user_id', 'numerical', 'integerOnly'=>true),

			array('name', 'length', 'max'=>256),

			array('description', 'length', 'max'=>2000),

			array('create_time, update_time', 'safe'),

			// The following rule is used by search().

			// Please remove those attributes that should not be searched.

			array('id, name, description, project_id, type_id, status_id, owner_id, requester_id, create_time, create_user_id, update_time, update_user_id', 'safe', 'on'=>'search'),

		);

	}


	/**

	 * @return array relational rules.

	 */

	public function relations()

	{

		// NOTE: you may need to adjust the relation name and the related

		// class name for the relations automatically generated below.

		return array(

			'requester' => array(self::BELONGS_TO, 'User', 'requester_id'),

			'owner' => array(self::BELONGS_TO, 'User', 'owner_id'),

			'project' => array(self::BELONGS_TO, 'Project', 'project_id'),

		);

	}


	/**

	 * @return array customized attribute labels (name=>label)

	 */

	public function attributeLabels()

	{

		return array(

			'id' => 'ID',

			'name' => 'Name',

			'description' => 'Description',

			'project_id' => 'Project',

			'type_id' => 'Type',

			'status_id' => 'Status',

			'owner_id' => 'Owner',

			'requester_id' => 'Requester',

			'create_time' => 'Create Time',

			'create_user_id' => 'Create User',

			'update_time' => 'Update Time',

			'update_user_id' => 'Update User',

		);

	}


	/**

	 * Retrieves a list of models based on the current search/filter conditions.

	 * @return CActiveDataProvider the data provider that can return the models based on the search/filter conditions.

	 */

	public function search()

	{

		// Warning: Please modify the following code to remove attributes that

		// should not be searched.


		$criteria=new CDbCriteria;


		$criteria->compare('id',$this->id);

		$criteria->compare('name',$this->name,true);

		$criteria->compare('description',$this->description,true);

		$criteria->compare('project_id',$this->project_id);

		$criteria->compare('type_id',$this->type_id);

		$criteria->compare('status_id',$this->status_id);

		$criteria->compare('owner_id',$this->owner_id);

		$criteria->compare('requester_id',$this->requester_id);

		$criteria->compare('create_time',$this->create_time,true);

		$criteria->compare('create_user_id',$this->create_user_id);

		$criteria->compare('update_time',$this->update_time,true);

		$criteria->compare('update_user_id',$this->update_user_id);


		return new CActiveDataProvider(get_class($this), array(

			'criteria'=>$criteria,

		));

	}

	

	public function getTypeOptions()

	{

		return array(

			self::TYPE_BUG => 'Bug',

			self::TYPE_FEATURE  => 'Feature',

			self::TYPE_TASK => 'Task',

		);

	}

	

	public function getStatusOptions()

	{

		return array(

			self::STATUS_NOT_YET_STARTED => 'Not Yet Started',

			self::STATUS_STARTED => 'Started',

			self::STATUS_FINISHED => 'Finished',

		);

	}

	

	/**

	 * @return string the status text display for the current issue

	 */ 

	public function getStatusText()

	{

		$statusOptions=$this->statusOptions;

		return isset($statusOptions[$this->status_id]) ? $statusOptions[$this->status_id] : "unknown status ({$this->status_id})";

	}


	/**

	 * @return string the type text display for the current issue

	 */ 

	public function getTypeText()

	{

		$typeOptions=$this->typeOptions;

		return isset($typeOptions[$this->type_id]) ? $typeOptions[$this->type_id] : "unknown type ({$this->type_id})";

	}

}



Can anyone help me with this little "issue" please?

Can you ensure that your fixture file is in place?

That is, please make sure you have a file which is located @ and named:

protected/tests/fixtures/tbl_issue.php

and contains the following content:


<?php


return array(

	'issueBug'=>array(

		'name' => 'Test Bug 1',

		'description' => 'This is test bug for project 1',

		'project_id' => 1,

		'type_id' => 0,

		'status_id' => 1,

		'owner_id' => 1,

		'requester_id' => 2,

		'create_time' => '',

		'create_user_id' => '',

		'update_time' => '',

		'update_user_id' => '',

	),

	'issueFeature'=>array(

		'name' => 'Test Feature 1',

		'description' => 'This is test feature for project 2',

		'project_id' => 2,

		'type_id' => 1,

		'status_id' => 0,

		'owner_id' => 2,

		'requester_id' => 1,

		'create_time' => '',

		'create_user_id' => '',

		'update_time' => '',

		'update_user_id' => '',

	),

);

Yes I should have mentioned that I have the fixture file indeed. But for some reason, I never see the issues in the database. That might be where the problem comes from…

And you have your test database configured as a different database from the development one…and ensured that the test database objects are in sync with the development one?

I would take a closer look at your fixture data. When I run the code for this test, it runs fine, However, I am able reproduce the issue you are seeing:

Exception: Unknown method ‘issues’ for class ‘IssueTest’.

if:

  1. There is no fixture file at all

or

  1. There is a fixture file, but no data/content defined (i.e. it returns an empty array)

This issue is weird. The fixture file (tb_issues.php) exists and is not empty:

tbl_issues.php:




<?php


return array(

	'issueBug'=>array(

		'name' => 'Test Bug 1',

		'description' => 'This is test bug for project 1',

		'project_id' => 1,

		'type_id' => 0,

		'status_id' => 1,

		'owner_id' => 1,

		'requester_id' => 2,

		'create_time' => '',

		'create_user_id' => '',

		'update_time' => '',

		'update_user_id' => '',

	),

	'issueFeature'=>array(

		'name' => 'Test Feature 1',

		'description' => 'This is test feature for project 2',

		'project_id' => 2,

		'type_id' => 1,

		'status_id' => 0,

		'owner_id' => 2,

		'requester_id' => 1,

		'create_time' => '',

		'create_user_id' => '',

		'update_time' => '',

		'update_user_id' => '',

	),

	'issueFeature2'=>array(

		'name' => 'Test Feature For Project 3',

		'description' => 'This is a test feature issue associated with project # 3 that is completed',

		'project_id' => 3,

		'type_id' => 1,

		'status_id' => 2,

		'owner_id' => 1,

		'requester_id' => 1,

		'create_time' => '',

		'create_user_id' => '',

		'update_time' => '',

		'update_user_id' => '',

	),

	

);



and here is my test.php config file:

test.php:




<?php

return CMap::mergeArray(

	require(dirname(__FILE__).'/main.php'),

	array(

		'components'=>array(

			'fixture'=>array(

				'class'=>'system.test.CDbFixtureManager',

			),

			'db'=>array(

				'connectionString' => 'mysql:host=localhost;dbname=yii_trackstar_test',

				'emulatePrepare' => true,

				'username' => 'root',

				'password' => 'root',

				'charset' => 'utf8',

				'tablePrefix' => 'tbl_'

			),

		),

	)

);



At first, the tablePrefix key was not present. I added it yesterday, but that didn’t fix the problem; I also added the tablePrefix key to the main config file.

I also replaced Yii 1.1.4 (which I was using before) with Yii 1.1.2. That didn’t fix the problem either.

It might be that you have defined some of your table columns to not allow NULL, but the fixture data is attempting to insert NULL values (say for the create_time or create_user_id fields). So, there might be an underlying DB error that is preventing the fixture manager object from being proper created.

One thing you could do is to fire up the yiic interactive shell, loading your test config




% YIIROOT/framework/yiic shell protected/config/test.php 

And then see if you can retrieve an instance of the fixture mgr:


>> print_r(Yii::app()->getComponent('fixture'));

You should get back a CDbFixtureManager Object. If not this may display a DB error which will help you further troubleshoot the issue.

I was writing a response with the output of the command you gave me and then I found what was wrong by chance. As I suspected it was a stupid typo issue. On the Yii side, the name of the "issues" table is tbl_issues. On the database side the name is tbl_issue.

Anyway, THANKS A LOT for your help, and especially your patience.

EDIT: solved the problem. It was indeed I typo I noticed after an apparently much needed cup of coffee. :rolleyes: The IssueTest.php class should start with a public $fixtures, instead of $fixture. I’ll leave the post in case someone else might find it useful, unless a moderator rather deletes it.

Hi everyone,

I have discovered Yii recently and am working through the book now as well. I ran into the except same problem as the topic-starter. So far I have checked and confirmed the following:

  • I am using Yii 1.2, in order to follow the book with minimum problems

  • the fixture file is in place and returns the neccesary array

  • fixture is not added to the database

  • I am using a seperate test database (per the book) which is working fine up to this point

  • I reproduced Jeff’s steps in reply #6 and get back a CDbFixtureManager object

  • the IssueTest.php unit tests does not fail when testGetStatusText() and testGetTypeText() are commented out.

Here is the code that I have.

protected/models/Issue.php




<?php


/**

 * This is the model class for table "tbl_issue".

 */

class Issue extends CActiveRecord

{

	/**

	 * The followings are the available columns in table 'tbl_issue':

	 * @var integer $id

	 * @var string $name

	 * @var string $description

	 * @var integer $project_id

	 * @var integer $type_id

	 * @var integer $status_id

	 * @var integer $owner_id

	 * @var integer $requester_id

	 * @var string $create_time

	 * @var integer $create_user_id

	 * @var string $update_time

	 * @var integer $update_user_id

	 */


         const TYPE_BUG = 0;

         const TYPE_FEATURE = 1;

         const TYPE_TASK = 2;

         const STATUS_NOT_YET_STARTED = 0;

         const STATUS_STARTED = 1;

         const STATUS_FINISHED = 2;


	// ... GII generated stuff omitted for clarity


        public function getTypeOptions()

        {

            return array(

                self::TYPE_BUG => 'Bug',

                self::TYPE_FEATURE => 'Feature',

                self::TYPE_TASK => 'Task',

            );

        }


        public function getStatusOptions()

        {

            return array(

                self::STATUS_NOT_YET_STARTED => 'Not yet started',

                self::STATUS_STARTED => 'Started',

                self::STATUS_FINISHED => 'Finished',

            );

        }


        public function getStatusText()

        {

            $statusOptions = $this->statusOptions;

            return isset($statusOptions[$this->status_id]) ?

                    $statusOptions[$this->status_id] :

                    "unknown status ({$this->status_id})";

        }


        public function getTypeText()

        {

            $typeOptions = $this->typeOptions;

            return isset($typeOptions[$this->type_id]) ?

                    $typeOptions[$this->type_id] :

                    "unknown type ({$this->type_id})";

        }

}



protected/tests/unit/IssueTest.php


<?php

class IssueTest extends CDbTestCase

{

    public $fixture = array(

        'projects' => 'Project',

        'issues' => 'Issue',

    );


    public function testGetTypes()

    {

        $options = Issue::model()->typeOptions;

        $this->assertTrue(is_array($options));

        $this->assertTrue(3 == count($options));

        $this->assertTrue(in_array('Bug', $options));

        $this->assertTrue(in_array('Feature', $options));

        $this->assertTrue(in_array('Task', $options));

    }


    public function testGetStatuses()

    {

        $options = Issue::model()->statusOptions;

        $this->assertTrue(is_array($options));

        $this->assertTrue(3 == count($options));

        $this->assertTrue(in_array('Not yet started', $options));

        $this->assertTrue(in_array('Started', $options));

        $this->assertTrue(in_array('Finished', $options));

    }


    public function testGetStatusText()

    {

        $this->assertTrue('Started' == $this->issues('issueBug')->getStatusText());

    }


    public function testGetTypeText()

    {

        $this->assertTrue('Bug' == $this->issues('issueBug')->getTypeText());

    }

}

It’s probably some really silly typo, but I can’t seem to find it. Any help would be much appreciated. Thanks!

No solution?

In IssueTest.php<br />

<br />

replace <br />


class IssueTest extends CTestCase

<br />

<br />

with<br />


class IssueTest extends CDbTestCase