Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Testing basics

An automated test is code that checks other code: it opens a page or calls a function and compares the result with the expected one. The test suite is run with one command after every change, and you see at once whether something broke. A CodeIgniter project already has PHPUnit — the program that runs tests — configured, and the tests themselves live in the tests folder.

The first test

Create the file tests/unit/BlogHelperTest.php by hand. It checks the reading_time() function from the lesson about helpers:

// tests/unit/BlogHelperTest.php
use CodeIgniter\Test\CIUnitTestCase;
 
class BlogHelperTest extends CIUnitTestCase
{
    public function testShortTextTakesOneMinute(): void
    {
        helper('blog');
 
        $this->assertSame('1 min read', reading_time('A short text'));
    }
}

The file and class name ends in Test, the method name starts with test. assertSame() compares the expected value with the actual one.

Running

vendor/bin/phpunit

On Windows the command is written as vendor\bin\phpunit. The terminal prints a dot for every passed test and a summary: OK (1 test, 1 assertion). If a check fails, the expected and actual values and the test line are shown.

Testing a page

The FeatureTestTrait trait lets you "open" a site address without a browser:

// tests/feature/HomeTest.php
use CodeIgniter\Test\CIUnitTestCase;
use CodeIgniter\Test\FeatureTestTrait;
 
class HomeTest extends CIUnitTestCase
{
    use FeatureTestTrait;
 
    public function testLoginPageOpens(): void
    {
        $result = $this->get('login');
 
        $result->assertStatus(200);
        $result->assertSee('Log in');
    }
}

The get() method performs a request to a route and returns the response. The test checks that the login page opens with the 200 code and contains the words "Log in".

AssertionWhat it states
assertStatus(200)The status code of the response
assertSee('text')The text is on the page
assertDontSee('text')The text is not on the page
assertRedirect()The response is a redirect
assertSessionHas('user_id')The session has the value

A test with a database

Tests work with a separate database — the tests connection from app/Config/Database.php — so that real data is not touched. The DatabaseTestTrait trait runs the migrations before the tests:

// tests/database/PostModelTest.php
use App\Models\PostModel;
use CodeIgniter\Test\CIUnitTestCase;
use CodeIgniter\Test\DatabaseTestTrait;
 
class PostModelTest extends CIUnitTestCase
{
    use DatabaseTestTrait;
 
    protected $migrate = true;
    protected $refresh = true;
    protected $namespace = 'App';
 
    public function testInsertSavesPost(): void
    {
        model(PostModel::class)->insert(['title' => 'Test post', 'body' => 'Text']);
 
        $this->seeInDatabase('posts', ['title' => 'Test post']);
    }
}

$namespace = 'App' says the migrations are taken from the app folder. seeInDatabase() checks that the table has a row with these values. By default the test database is SQLite in memory, which needs the sqlite3 PHP extension.

What to test

Start with what is most costly to break: logging in, saving a post, protected pages. A good test checks one statement and does not depend on other tests.

Common mistake

Database tests must not be pointed at the working connection: the $refresh property rolls the migrations back and drops the tables before every test. Do not replace the tests group in app/Config/Database.php with the details of the main database. If you need MySQL, create a separate empty database for tests, for example blog_test.

Thanks for reading!

Donate to the site — we work hard for you.