Cookbook

Testing

Quick patterns for testing Glueful apps with PHPUnit

Quick patterns for testing Glueful apps using PHPUnit. This guide reflects the currently implemented testing surface in the framework repository.

PHPUnit Setup

Core configuration lives in phpunit.xml and points to tests/bootstrap.php for environment bootstrapping.

What bootstrap does today:

AspectBehavior
AutoloadLocates Composer autoload in project or parent
ContainerBuilds a minimal container and exposes it globally via app() helper
LoggingInjects a mock logger (no file/db writes)
Env VarsAPP_ENV=testing, in-memory sqlite (:memory:) via DB_CONNECTION=sqlite, disables file/db logging
HelpersDefines config(), base_path(), app() if not already defined
TimezoneForces UTC for deterministic timestamps
DirectoriesEnsures tests/storage/cache & tests/storage/logs exist
Exception HandlingPuts ExceptionHandler (if present) into test mode

CI: If you maintain GitHub Actions, ensure workflow installs dependencies then invokes composer test. (Exact workflow file may vary; adjust doc if you rename pipelines.)

Running Tests

# All tests (script must exist in composer.json)
composer test

# Individual suites (verify these scripts exist; otherwise use --testsuite)
composer test:unit
composer test:integration

# Or directly via PHPUnit:
vendor/bin/phpunit --testsuite Unit
vendor/bin/phpunit --testsuite Integration

# Coverage (if Xdebug or PCOV enabled)
vendor/bin/phpunit --coverage-html coverage

Suites defined: Unit, Integration, Feature, Performance (see phpunit.xml). If composer scripts are not yet defined add them, e.g.:

"scripts": {
    "test": "phpunit",
    "test:unit": "phpunit --testsuite Unit",
    "test:integration": "phpunit --testsuite Integration"
}

Base TestCase

Glueful\Testing\TestCase provides:

FeatureDescription
Automatic app bootCalls Framework::create(...)->withEnvironment('testing')->boot() (ensure Framework class exists)
Container access$this->get(<Service>::class) convenience wrapper
Refresh app$this->refreshApplication() reconstructs a fresh application instance
CleanupClears global framework state after each test

Example (adjust service names if namespaces differ):

<?php
use Glueful\Testing\TestCase;

final class ExampleTest extends TestCase
{
    public function test_application_boots(): void
    {
        $this->assertNotNull($this->app());
        $this->assertTrue($this->has(\Glueful\Logging\LogManager::class));
    }
}

If Glueful\Framework or routing layer is not yet implemented in your tree, either stub them or override createApplication() in a subclass to customize initialization.

HTTP / Routing Example (Pending Implementation)

If you have a Router service registered and a Response object:

<?php
use Glueful\Testing\TestCase;
use Symfony\Component\HttpFoundation\Request;

final class PingRouteTest extends TestCase
{
    public function test_ping_returns_ok(): void
    {
        $router = $this->get(\Glueful\Routing\Router::class); // Ensure this service exists
        $router->get('/ping', fn() => new \Glueful\Http\Response(['ok' => true]));

        $response = $router->dispatch(Request::create('/ping', 'GET'));
        $this->assertSame(200, $response->getStatusCode());
    }
}

If routing is not integrated yet, skip this section until the routing package is added.

Environment Helpers

Within tests you can leverage:

HelperPurpose
app()Access global container (set in bootstrap)
config()Returns minimal config values mocked in bootstrap
base_path()Project root resolution

Use these when you need quick access without depending on full framework facades.

Planned Enhancements (Optional Roadmap)

Potential improvements to expand testing ergonomics:

IdeaValue
HTTP test client ($this->getJson())Simplify request simulation
DB transaction traitAutomatic rollback for integration tests
In-memory mailer spyAssert outbound notifications
Fixture/seeder utilitiesDeterministic test data creation
Snapshot assertionsStable structural comparisons

Contribute these incrementally; keep docs updated alongside new helpers.

Troubleshooting

IssueCauseFix
Container service missingService not registered in boot chainExtend createApplication() or add to container before test
Coverage emptyXdebug/PCOV not enabledInstall and enable coverage driver
Global state leakageNot using base TestCase or custom static cachesEnsure teardown clears statics or call refreshApplication()
Class not found (Framework/Router)Component not yet added to repoImplement stub or adjust TestCase to skip boot path

Summary

You now have a foundational testing layer: environment isolation, a reusable base TestCase, and clear extension points. As you add routing, HTTP helpers, or database lifecycle utilities, expand this document instead of rewriting examples inline across the codebase.