Tina4

Testing {#testing}#

Tina4 projects use PHPUnit for testing. Each module in the Tina4 ecosystem includes a phpunit.xml configuration and a tests/ directory.

Hot tips!

  • Use SQLite (:memory:) for database tests, no external services needed
  • Run composer test from any Tina4 module to execute its test suite
  • All Tina4 modules target PHPUnit 9 with PHP 8.1+

Setting Up Tests {#setup}#

Install PHPUnit#

PHPUnit is included as a dev dependency in Tina4 modules:

bash
composer require --dev phpunit/phpunit ^9

Create phpunit.xml#

xml
<?xml version="1.0" encoding="UTF-8"?><phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.5/phpunit.xsd"         bootstrap="vendor/autoload.php"         colors="true"         verbose="true">    <testsuites>        <testsuite name="default">            <directory>tests</directory>        </testsuite>    </testsuites></phpunit>

Add a Test Script to composer.json#

json
{    "scripts": {        "test": "./vendor/bin/phpunit tests --color"    }}

Writing Tests {#writing-tests}#

Basic Test#

php
<?phpโ€‹namespace Tests;โ€‹use PHPUnit\Framework\TestCase;โ€‹class MyTest extends TestCase{    public function testExample(): void    {        $this->assertTrue(true);    }}

Testing with a Database#

Use SQLite in-memory for fast, isolated database tests:

php
<?phpโ€‹namespace Tests;โ€‹use PHPUnit\Framework\TestCase;use Tina4\DataSQLite3;โ€‹class DatabaseTest extends TestCase{    private $DBA;โ€‹    protected function setUp(): void    {        $this->DBA = new DataSQLite3(":memory:");        $this->DBA->exec("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)");        $this->DBA->exec("INSERT INTO users (name, email) VALUES ('John', 'john@example.com')");    }โ€‹    public function testFetchUsers(): void    {        $result = $this->DBA->fetch("SELECT * FROM users");        $this->assertEquals(1, $result->getNoOfRecords());    }โ€‹    public function testInsertUser(): void    {        $this->DBA->exec("INSERT INTO users (name, email) VALUES ('Jane', 'jane@example.com')");        $result = $this->DBA->fetch("SELECT * FROM users");        $this->assertEquals(2, $result->getNoOfRecords());    }}

Testing ORM Objects#

php
<?phpโ€‹namespace Tests;โ€‹use PHPUnit\Framework\TestCase;use Tina4\DataSQLite3;โ€‹class ORMTest extends TestCase{    protected $DBA;โ€‹    protected function setUp(): void    {        global $DBA;        $DBA = new DataSQLite3(":memory:");        $this->DBA = $DBA;โ€‹        $this->DBA->exec("CREATE TABLE user (            id INTEGER PRIMARY KEY AUTOINCREMENT,            first_name TEXT,            last_name TEXT,            email TEXT        )");    }โ€‹    public function testSaveAndLoad(): void    {        $user = new \App\User();        $user->firstName = "John";        $user->lastName = "Doe";        $user->email = "john@example.com";        $user->save();โ€‹        $loaded = (new \App\User())->load("id = ?", [1]);        $this->assertEquals("John", $loaded->firstName);        $this->assertEquals("john@example.com", $loaded->email);    }}

Testing Routes#

php
<?phpโ€‹namespace Tests;โ€‹use PHPUnit\Framework\TestCase;โ€‹class RouteTest extends TestCase{    public function testGetRoute(): void    {        // Register the route        \Tina4\Get::add("/api/test", function(\Tina4\Response $response) {            return $response(["status" => "ok"]);        });โ€‹        // Simulate a request        $result = \Tina4\Routing::callRoute("/api/test", "GET");        $this->assertStringContainsString("ok", $result);    }}

Running Tests {#running}#

bash
# Run all testscomposer testโ€‹# Run a specific test file./vendor/bin/phpunit tests/MyTest.phpโ€‹# Run with verbose output./vendor/bin/phpunit tests -vvv --colorโ€‹# Run a specific test method./vendor/bin/phpunit --filter testExample tests/MyTest.phpโ€‹# Generate JUnit XML report./vendor/bin/phpunit tests --log-junit=tests/junit.xml

Test Organization {#organization}#

A typical Tina4 project test structure:

project/  tests/    bootstrap.php         # Optional: custom setup before tests    DatabaseTest.php      # Database layer tests    ORMTest.php          # ORM model tests    RouteTest.php        # API endpoint tests    ServiceTest.php      # Business logic tests  phpunit.xml            # PHPUnit configuration

Bootstrap File#

If you need custom setup (e.g., defining constants, connecting to a test database):

php
<?php// tests/bootstrap.phpโ€‹if (!isset($_SERVER['DOCUMENT_ROOT'])) {    $_SERVER['DOCUMENT_ROOT'] = dirname(__DIR__);}โ€‹require_once dirname(__DIR__) . '/vendor/autoload.php';

Update phpunit.xml to use it:

xml
<phpunit bootstrap="tests/bootstrap.php" ...>

CI Integration {#ci}#

All Tina4 modules include GitHub Actions CI. A typical workflow:

yaml
name: Testsโ€‹on:  push:    branches: [main]  pull_request:    branches: [main]โ€‹jobs:  test:    runs-on: ubuntu-latest    strategy:      matrix:        php-version: ['8.1', '8.2', '8.3', '8.4']โ€‹    steps:      - uses: actions/checkout@v4      - uses: shivammathur/setup-php@v2        with:          php-version: ${{ matrix.php-version }}      - run: composer install --no-progress      - run: composer test