Search code examples
phpobjectyiiphpunitfixtures

How to create a Model object in Yii PHPUnit test without a fixture?


I am writing a PHPUnit test for my Yii application. I read here:

Tip: Having too many fixture files could increase the test time dramatically. For this reason, you should only provide fixture files for those tables whose content may change during the test. Tables that serve as look-ups do not change and thus do not need fixture files.

I indeed have a large fixture (180 records, which takes >20 seconds to load), which is only used as a look-up. However, I do need to transform it easily from an associative array into a Model object, like you can usually do with the fixture syntax below. The tip suggests that there is also a way to create a Model object without the use of a fixture, but does not mention how this is done. Can anyone help out?

Creation of Model object with a fixture:

// tests/fixtures/Order.php
return array(
    'row_id' => array(
        'id' => 1,
        'name' => 'hello',
    )
)

// tests/unit/AbcTest.php
public $fixtures = array(
    'orders' => 'Order',
)

public test_abc()
{
    $order = $this->orders('row_id');
    ....
}

Solution

  • Another option:
    when you create db migration you should apply it on production db and on test db moreover you should populate test tables with test data.

    Benefits of this approach:

    1. You will run populate sql just once (not like fixture - each time invokes test).
    2. Your test will executes fast, because db will be prepared.
    3. When you commit new feature that need new test with new data in db - you create db migration, and it will be executed just once, in explicit way.

    For example:

    <?php
    
    class m150608_110143_init extends CDbMigration
    {
        public function safeUp()
        {
            $sql1 = "
                CREATE TABLE brand (
                    id INT AUTO_INCREMENT,
                    name VARCHAR(100) NOT NULL DEFAULT '',
                    country VARCHAR(50) NOT NULL DEFAULT '',
                    PRIMARY KEY (id)
                );
            ";
            $sql2 = "
                INSERT INTO brand VALUES
                    (null, 'aston martin', 'UK'),
                    (null, 'audi', 'Germany'),
                    (null, 'bmw', 'Germany'),
                    (null, 'citroen', 'France'),
                    (null, 'peugeot', 'France'),
                    (null, 'porsche', 'Germany'),
                    (null, 'toyota', 'Japan'),
                    (null, 'ferrari', 'Italy')
                ;
            ";
            // Production db.
            $this->setDbConnection(Yii::app()->db);
            $this->execute($sql1);
            // Test db.
            $this->setDbConnection(Yii::app()->dbUnitTest);
            $this->execute($sql1);
            // Populate test db with fixtures.
            $this->execute($sql2);
            return true;
        }
    
        public function down()
        {
            $sql = 'DROP TABLE brand;';
            // Test db.
            $this->setDbConnection(Yii::app()->dbUnitTest);
            $this->execute($sql);
            // Production db.
            $this->setDbConnection(Yii::app()->db);
            $this->execute($sql);
            return true;
        }
    }
    

    And in test you don't have to think about fixtures.