Testing
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:
Aspect | Behavior |
---|---|
Autoload | Locates Composer autoload in project or parent |
Container | Builds a minimal container and exposes it globally via app() helper |
Logging | Injects a mock logger (no file/db writes) |
Env Vars | APP_ENV=testing , in-memory sqlite (:memory: ) via DB_CONNECTION=sqlite , disables file/db logging |
Helpers | Defines config() , base_path() , app() if not already defined |
Timezone | Forces UTC for deterministic timestamps |
Directories | Ensures tests/storage/cache & tests/storage/logs exist |
Exception Handling | Puts 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:
Feature | Description |
---|---|
Automatic app boot | Calls 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 |
Cleanup | Clears 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:
Helper | Purpose |
---|---|
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:
Idea | Value |
---|---|
HTTP test client ($this->getJson() ) | Simplify request simulation |
DB transaction trait | Automatic rollback for integration tests |
In-memory mailer spy | Assert outbound notifications |
Fixture/seeder utilities | Deterministic test data creation |
Snapshot assertions | Stable structural comparisons |
Contribute these incrementally; keep docs updated alongside new helpers.
Troubleshooting
Issue | Cause | Fix |
---|---|---|
Container service missing | Service not registered in boot chain | Extend createApplication() or add to container before test |
Coverage empty | Xdebug/PCOV not enabled | Install and enable coverage driver |
Global state leakage | Not using base TestCase or custom static caches | Ensure teardown clears statics or call refreshApplication() |
Class not found (Framework/Router) | Component not yet added to repo | Implement 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.