Testing with fixtures

I have trouble learning how to run tests with fixtures. I have the following fixture-file, but it doesn’t seem to load the defined records, the test returns bool as result when testing the get function in the model. Is there some command I need to run to load the records into the fixture?

<?php
namespace App\Test\Fixture;

use Cake\TestSuite\Fixture\TestFixture;

class StatusFixture extends TestFixture {

    public $fields = [
        'id' => ['type' => 'integer'],
        'type' => 'text',
        'date' => 'text',
        'start_time' => 'text',
        'service_id' => ['type' => 'integer'],
        'date_created' => ['type' => 'datetime'],
        'date_modified' => ['type' => 'datetime'],
        'manned_by' => ['type' => 'integer'],
        'text' => 'text',
        'active' => 'boolean',
        '_constraints' => [
            'primary' => ['type' => 'primary', 'columns' => ['id']],
        ]
    ];

    public $records = [
        [
            'id' => 1,
            'type' => 'bookable',
            'date' => '20200529',
            'start_time' => '16:00',
            'service_id' => 1,
            'date_created' => '1970-01-01 00:00:01',
            'date_modified' => '1970-01-01 00:00:01',
            'manned_by' => 1,
            'text' => '',
            'active' => 1,
        ]
    ];
}

Every file with implementations of tests that are going to use this fixture needs to be told to load the fixture. See https://book.cakephp.org/3/en/development/testing.html#loading-fixtures-in-your-test-cases

Yes, I do with public $import = [‘app.Status’]; in the test class, but it still doesn’t seem to load the records specified in the fixture file.

Maybe it’s the way I’m loading the table? I load it like this, but maybe it loads the actual Table with database, instead of the fixture?:

namespace App\Test\TestCase\Model\Table;

use App\Model\Table\StatusTable;
use Cake\ORM\TableRegistry;
use Cake\TestSuite\TestCase;

class StatusTableTest extends TestCase
{
    public $fixtures = ['app.Status'];

    public function testGetBookings()
    {
        $this->Status = TableRegistry::getTableLocator()->get('Status');
        ...
        $this->assertEquals($expected, $result);
    }
}

That looks right. The problem might be in the ... part? You’re doing a get on ID 1? You said the test returns false; is it the test that fails (e.g. by an exception being thrown due to the table or record not existing) or the assertion? The latter might be if your $expected has strings in the date fields, for example, where the $result would presumably have some sort of Date objects.

Yeah, I get on ID 1. The test result says that the result is empty, and then the assertion fails too.
If I do an import I get the table with records, but the import deletes the table which I want to keep, even when I specify dropTable = false.

Things I would do to further test this:

  • Put a “sleep” in the test itself, long enough for you to look at the test database manually and see if the table is created and record inserted.
  • Put debugging output in places like the fixture constructor (might need to be created just for this purpose; make sure you remember to call the parent) to confirm that the fixture is actually being called as you expect.

When debugging/printing the result, it shows the content of the table in the booking-schema, not the booking_test-schema. My code looks like following:

//app_local.php
'Datasources' => [
    'default' => [
        'schema' => 'booking',
    ],
    'test' => [
        'schema' => 'booking_test',
    ],
],

<?php
namespace App\Test\Fixture;

use Cake\TestSuite\Fixture\TestFixture;

class StatusFixture extends TestFixture {

    public $connection = 'test';

    public $fields = [
        'id' => ['type' => 'integer'],
        'type' => 'text',
        'date' => 'text',
        'start_time' => 'text',
        'service_id' => ['type' => 'integer'],
        'date_created' => ['type' => 'datetime'],
        'date_modified' => ['type' => 'datetime'],
        'manned_by' => ['type' => 'integer'],
        'text' => 'text',
        'active' => 'boolean',
        '_constraints' => [
            'primary' => ['type' => 'primary', 'columns' => ['id']],
        ]
    ];

    public function init(): void {
        $this->table = 'Status';
        $this->records = [
            [
                'type' => 'bookable',
                'date' => '20200529',
                'start_time' => '16:00',
                'service_id' => 1,
                'date_created' => '1970-01-01 00:00:01',
                'date_modified' => '1970-01-01 00:00:01',
                'manned_by' => 1,
                'text' => '',
                'active' => 1,
            ],
        ];
        parent::init();
    }
}

<?php
namespace App\Test\TestCase\Model\Table;

use App\Model\Table\UserTable;
use Cake\Datasource\ConnectionManager;
use Cake\ORM\TableRegistry;
use Cake\TestSuite\TestCase;

class StatusTableTest extends TestCase {

    public $fixtures = ['app.Status'];

    function testGetStatus() {
        $q = "SELECT * FROM status";
        $result = ConnectionManager::get('test')->execute($q)->fetchAll('assoc');
        print_r($result);
    }
}

I think I need a separate test database…

You’re trying to run tests in the same database as your “live” data? Tests, by their nature, create empty tables, populate them with the fixture data, and then destroy the table when they are done. That’s definitely not something you want happening in your main database.

Yeah, sorry for not realizing this. I thought fixtures existed in memory, or a separate schema was it.

Hi,

you may also consider testing without using the CakePHP Fixture, which I found a bit heavy to use and maintain.

The fixture factories: https://github.com/pakacuda/cakephp-fixture-factories

You will be able to populate your test DB without any CakePHP native fixtures. The documentation is in the package.