Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Testing basics

A test is code that checks other code: it opens a page, submits a form and compares the result with the expected one. Tests are run with one command after every change and show at once whether something broke. CakePHP uses the PHPUnit library, which is already installed in the project.

Where they live

FolderWhat it holds
tests/TestCase/ControllerController tests
tests/TestCase/Model/TableTable tests
tests/FixtureFixtures, the test data

The test database

Tests work with a separate database so that real data is not damaged. Create an empty blog_test database and set it in the test connection:

// config/app_local.php
'Datasources' => [
    'test' => [
        'host' => 'localhost',
        'username' => 'root',
        'password' => 'secret',
        'database' => 'blog_test',
    ],
],

Its tables are created before the tests start, in the tests/bootstrap.php file. To build them from your migrations, replace the SchemaLoader line there with (new \Migrations\TestSuite\Migrator())->run();.

Creating a test

bin/cake bake test Controller Articles

The command creates tests/TestCase/Controller/ArticlesControllerTest.php with method stubs. Keep a single test in it:

// tests/TestCase/Controller/ArticlesControllerTest.php
namespace App\Test\TestCase\Controller;
 
use Cake\TestSuite\IntegrationTestTrait;
use Cake\TestSuite\TestCase;
 
class ArticlesControllerTest extends TestCase
{
    use IntegrationTestTrait;
 
    public function testIndex(): void
    {
        $this->get('/articles');
 
        $this->assertResponseOk();
    }
}

get() makes a request to the application without a browser and assertResponseOk() checks that the response is successful. The name of a test method starts with the word test.

Running

vendor/bin/phpunit

PHPUnit prints a dot for every passed test and a summary: OK (1 test, 1 assertion). On failure there is an F instead of a dot and a description of what did not match.

Fixtures

A fixture fills a table before every test and clears it afterwards. It is created with bin/cake bake fixture Articles:

// tests/Fixture/ArticlesFixture.php
namespace App\Test\Fixture;
 
use Cake\TestSuite\Fixture\TestFixture;
 
class ArticlesFixture extends TestFixture
{
    public array $records = [
        [
            'title' => 'First article',
            'body' => 'Article text.',
            'published' => true,
            'created' => '2026-03-01 10:00:00',
            'modified' => '2026-03-01 10:00:00',
        ],
    ];
}

Attach it in the test and check that the article is visible on the page:

protected array $fixtures = ['app.Articles'];
 
public function testIndex(): void
{
    $this->get('/articles');
 
    $this->assertResponseOk();
    $this->assertResponseContains('First article');
}

Checking a form

public function testAdd(): void
{
    $this->enableCsrfToken();
 
    $this->post('/articles/add', ['title' => 'New article', 'body' => 'Text']);
 
    $this->assertRedirect(['action' => 'index']);
}

enableCsrfToken() adds the form token to the request. The test checks that saving is followed by a redirect to the list.

Assertions

MethodWhat it checks
assertResponseOk()A 2xx response code
assertResponseCode(404)A specific code
assertResponseContains('…')Text in the response
assertRedirect(…)A redirect

Common mistake

If the test connection points to the working database, tests clear its tables. Before the first run make sure that Datasources.test holds a separate database with a different name.

Thanks for reading!

Donate to the site — we work hard for you.