views:

289

answers:

3

I'm not sure what I've done wrong in my CakePHP unit test configuration. Every time I run a test case, the model tables associated with my fixtures are missing form my test database.

After running an individual test case I have to re-import my database tables using phpMyAdmin.

Here are the relevant files:

This is the class I'm trying to test comment.php. This table is dropped after the test.

  App::import('Sanitize');

  class Comment extends AppModel{
  public $name      = 'Comment';

  public $actsAs    = array('Tree');

     public $belongsTo = array('User' => array('fields'=>array('id', 'username')));

  public $validate = array(
  'text'        => array(
  'rule'      =>array('between', 1, 4000),
  'required'  =>'true',
  'allowEmpty'=>'false',
  'message'   => "You can't leave your comment text empty!")
  );

database.php

class DATABASE_CONFIG {

var $default = array(
'driver'     => 'mysql',
'persistent' => false,
'host'       => 'project.db',
'login'      => 'projectman',
'password'   => 'projectpassword',
'database'   => 'projectdb',
'prefix'     => ''
);

var $test = array(
'driver'     => 'mysql',
'persistent' => false,
'host'       => 'project.db',
'login'      => 'projectman',
'password'   => 'projectpassword',
'database'   => 'testprojectdb',
'prefix'     => ''
 );
 }

My comment.test.php file. This is the table that keeps getting dropped.

<?php
App::import('Model', 'Comment');

class CommentTestCase extends CakeTestCase
{

  public $fixtures = array('app.comment');

  function start(){
    $this->Comment =& ClassRegistry::init('Comment');
    $this->Comment->useDbConfig = 'test_suite';
  }

This is my comment_fixture.php class:

  <?php
   class CommentFixture extends CakeTestFixture
   {
     var $name = "Comment";

     var $import = 'Comment';
   }

And just in case, here is a typical test method in the CommentTestCase class

   function testMsgNotificationUserComment(){
   $user_id       = '1';
    $submission_id = '1';
    $parent_id = $this->Comment->commentOnModel('Submission', $submission_id,     '0',    $user_id, "Says: A");

  $other_user_id = '2';
   $msg_id = $this->Comment->commentOnModel('Submission', $submission_id, $parent_id,      $other_user_id, "Says: B");

  $expected = array(array('Comment'=>array('id'=>$msg_id, 'text'=>"Says: B", 'submission_id'=>$submission_id, 'topic_id'=>'0', 'ack'=>'0')));

  $result = $this->Comment->getMessages($user_id);

  $this->assertEqual($result, $expected);
}

I've been dealing with this for a day now and I'm starting to be put off by CakePHP's unit testing. In addition to this issue, Servral times now I've had data inserted into by 'default' database configuration after running tests! What's going on with my configuration?!

A: 

I think that's the idea. Fixtures are fixed tables with fixed contents. They're not the tables you use otherwise in production or during development. That's why they have a separate connection in database.php, you're supposed to use a different database or at least a different prefix for test tables. These will be created based on your fixtures before each test and torn down afterwards, so you're always testing against a known set of data.

deceze
So then it is not possible to run a test case, for example CommentTestCase, and then immediately run CommentTestCase again?I'm simply trying to use the CakePHP test.php page to be able to run the CommentTestCase more than once without reimporting the entire database.
Frank
@Frank You can run it however many times in a row you want, given that you supply the data in fixtures. Test cases are not meant to be run just using the data that is in the database, the database setup (and teardown) is part of a test run.
deceze
@Frank You *can* tell a fixture to take its data from an existing table, but it'll still setup a separate table (at least you should make it do so) and tear it down at the end. I hope you have read the manual? http://book.cakephp.org/view/358/Preparing-test-data
deceze
Yeah, I've read the manual's testing section several times to try and figure out what's going on. You mentioned: "So long as you supply the data in the fixtures". In my test cases, I generate the data, not in the fixtures but in the test method. I guess that is incorrect and it HAS to be in the fixtures?
Frank
@Frank For the sake of separation, I think you should use fixtures to generate the data. It doesn't make too much of a difference in this case *where* you generate the data though, I suppose. The point is, if you're using fixtures, they'll tear down your test tables at the end of a test. I don't know enough about the particulars of Cake tests to tell you if or how you can forego the use of fixtures. Since you're probably not doing anything special though, you should fit your method into the established framework (use fixtures in separate tables).
deceze
A: 

Your database config looks right...

You shouldn't have to do

$this->Comment->useDbConfig = 'test_suite';

My Recommendation

You should use the cake bake shell to create the models for all of your models and let it overwrite them (obviously back up any custom models first). You don't need to go through the validation and association prompts (say "n"), but yes to all overwrites (again, backup your code first).

When you do that, it also creates a basic test case and a fixture for the model.

Use the test case it creates, and add to it...

What you need is to then call into being EVERY single fixture you use and all associated fixtures... all the hasMany and belongsTo and HABTM associations need the fixture loaded, but they don't automatically get called into being in the test suite.

note.test.php

<?php 
App::import('Model', 'Note');
class NoteTestCase extends CakeTestCase {
    var $Note = null;
    var $fixtures = array(
        'app.note',
        'app.version',
        'app.corp',
        'app.advertisement',
        'app.member',
        'app.member_detail',
        'app.member_newsletter',
        'app.groups_members_join',
        'app.group',
        'app.job',
        'app.job_category',
        'app.jobs_categories_join',
        );
    function startTest() {
        $this->Note =& ClassRegistry::init('Note');
    }
    function testNoteInstance() {
        $this->assertTrue(is_a($this->Note, 'Note'));
    }
    function testNoteFind() {
        $this->Note->recursive = -1;
        $results = $this->Note->find('first');
        $this->assertTrue(!empty($results));
        $expected = array('Note' => array(
            'id' => 1,
            'model' => 'Lorem ipsum dolor sit amet',
            'model_id' => 1,
            'created' => '2010-03-30 16:26:59',
            'member_id' => 1,
            'body' => 'Lorem ipsum dolor sit amet, aliquet feugiat. Convallis morbi fringilla gravida,phasellus feugiat dapibus velit nunc, pulvinar eget sollicitudin venenatis cum nullam,vivamus ut a sed, mollitia lectus. Nulla vestibulum massa neque ut et, id hendrerit sit,feugiat in taciti enim proin nibh, tempor dignissim, rhoncus duis vestibulum nunc mattis convallis.',
            'is_active' => 1
        ));
        $this->assertEqual($results, $expected);
    }
    function testJobWithNotes() {
        $this->Job =& ClassRegistry::init('Job');
        $uniqueValue = rand(0,time());
        $savedJob = $this->Job->saveAll(array(
            'Job' => array(
                'title' => "Test Job [$uniqueValue] from ".__FILE__,
            ),
            'Note' => array(
                array(
                    'model' => "Job",
                    'body' => "Here is a unique value [$uniqueValue]")
            )));
        $this->assertTrue($savedJob);
        $this->assertEqual($this->Note->find('first',array(
            'fields' => array('body'),
            'conditions' => array('model_id' => $this->Job->id, 'model' => $this->Job->name))),
            array('Note'=>array('body'=>"Here is a unique value [$uniqueValue]")));
    }
}
?>

And just in case you want it, here is

note_fixture.php

<?php 
class NoteFixture extends CakeTestFixture {
    var $name = 'Note';
    var $fields = array(
        'id' => array('type'=>'integer', 'null' => false, 'default' => NULL, 'key' => 'primary'),
        'model' => array('type'=>'string', 'null' => false, 'default' => NULL, 'length' => 32),
        'model_id' => array('type'=>'integer', 'null' => false, 'default' => NULL),
        'created' => array('type'=>'datetime', 'null' => false, 'default' => NULL),
        'member_id' => array('type'=>'integer', 'null' => false, 'default' => NULL),
        'body' => array('type'=>'text', 'null' => false, 'default' => NULL),
        'is_active' => array('type'=>'boolean', 'null' => false, 'default' => '1'),
        'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1))
    );
    var $records = array(array(
        'id' => 1,
        'model' => 'Lorem ipsum dolor sit amet',
        'model_id' => 1,
        'created' => '2010-03-30 16:26:59',
        'member_id' => 1,
        'body' => 'Lorem ipsum dolor sit amet, aliquet feugiat. Convallis morbi fringilla gravida,phasellus feugiat dapibus velit nunc, pulvinar eget sollicitudin venenatis cum nullam,vivamus ut a sed, mollitia lectus. Nulla vestibulum massa neque ut et, id hendrerit sit,feugiat in taciti enim proin nibh, tempor dignissim, rhoncus duis vestibulum nunc mattis convallis.',
        'is_active' => 1
    ));
}
?>
zeroasterisk
A: 

in your CakeTestCase class, just put a member dropTables:

<?php
App::import('Model', 'Comment');

class CommentTestCase extends CakeTestCase
{

  public $fixtures = array('app.comment');
  public $dropTables = false; // <- here it is 

  function start(){
    $this->Comment =& ClassRegistry::init('Comment');
    $this->Comment->useDbConfig = 'test_suite';
  }
challet