Testing

Running Tests

# Run all tests
composer test

# Run with verbose output
vendor/bin/phpunit --verbose

# Run a specific test class
vendor/bin/phpunit tests/Unit/ModelCollectionTest.php

# Run a specific test method
vendor/bin/phpunit --filter test_pluck_extracts_key_from_objects

Test Setup

The package includes a tests/bootstrap.php that provides mock WordPress functions so you can test without a running WordPress installation.

Bootstrap

// tests/bootstrap.php
require_once dirname(__DIR__) . '/vendor/autoload.php';

// WordPress mocks are provided:
// sanitize_title(), wp_hash_password(), current_time(), absint(), esc_sql()

Writing Unit Tests for Your Models

Testing ModelCollection

The ModelCollection class has no external dependencies and can be tested directly:

<?php

declare(strict_types=1);

namespace Tests\Unit;

use PHPUnit\Framework\TestCase;
use WPZylos\Framework\Model\ModelCollection;

class ModelCollectionTest extends TestCase
{
    public function test_pluck(): void
    {
        $items = [
            (object) ['name' => 'Alice'],
            (object) ['name' => 'Bob'],
        ];

        $collection = new ModelCollection($items);
        $this->assertSame(['Alice', 'Bob'], $collection->pluck('name'));
    }

    public function test_filter(): void
    {
        $collection = new ModelCollection([1, 2, 3, 4, 5]);
        $evens = $collection->filter(fn($n) => $n % 2 === 0);

        $this->assertSame([2, 4], $evens->all());
    }
}

Testing HasAttributes

Create a concrete stub to test the trait in isolation:

<?php

declare(strict_types=1);

namespace Tests\Unit;

use PHPUnit\Framework\TestCase;
use WPZylos\Framework\Model\Concerns\HasAttributes;

class HasAttributesTest extends TestCase
{
    private function makeStub(array $casts = []): object
    {
        return new class ($casts) {
            use HasAttributes;
            protected array $casts;

            public function __construct(array $casts)
            {
                $this->casts = $casts;
            }

            public function set(string $key, mixed $value): void
            {
                $this->setAttribute($key, $value);
            }

            public function get(string $key): mixed
            {
                return $this->getAttribute($key);
            }
        };
    }

    public function test_integer_cast(): void
    {
        $stub = $this->makeStub(['age' => 'int']);
        $stub->set('age', '25');

        $this->assertSame(25, $stub->get('age'));
    }

    public function test_dirty_tracking(): void
    {
        $stub = $this->makeStub();
        $stub->set('name', 'Original');
        $stub->syncOriginal();

        $stub->set('name', 'Changed');
        $this->assertTrue($stub->isDirty('name'));
    }
}

Testing Events

<?php

declare(strict_types=1);

namespace Tests\Unit;

use PHPUnit\Framework\TestCase;
use WPZylos\Framework\Model\Concerns\HasEvents;

class HasEventsTest extends TestCase
{
    protected function setUp(): void
    {
        // Concrete class for testing
        TestEventModel::flushEventListeners();
    }

    public function test_event_fires(): void
    {
        $fired = false;

        TestEventModel::registerModelEvent('creating', function () use (&$fired) {
            $fired = true;
        });

        $model = new TestEventModel();
        // ... trigger event

        $this->assertTrue($fired);
    }
}

Testing with Mocked Database

For integration-level tests, you can mock the Connection and QueryBuilder:

<?php

declare(strict_types=1);

namespace Tests\Unit;

use PHPUnit\Framework\TestCase;
use WPZylos\Framework\Core\Contracts\ApplicationInterface;
use WPZylos\Framework\Model\Model;

class ModelIntegrationTest extends TestCase
{
    private ApplicationInterface $app;

    protected function setUp(): void
    {
        $this->app = $this->createMock(ApplicationInterface::class);
        Model::setApplication($this->app);
    }

    protected function tearDown(): void
    {
        // Reset static state
        $ref = new \ReflectionClass(Model::class);
        $prop = $ref->getProperty('app');
        $prop->setAccessible(true);
        $prop->setValue(null, null);
    }

    public function test_get_application(): void
    {
        $this->assertSame($this->app, Model::getApplication());
    }
}

Code Coverage

Generate coverage reports:

# HTML report
vendor/bin/phpunit --coverage-html coverage/

# Text summary
vendor/bin/phpunit --coverage-text

Static Analysis

# Run PHPStan
composer analyze

# Check code style
composer cs

# Fix code style
composer cs-fix

# Run all quality checks
composer qa