phalcon/talon

Test harness and Phalcon bootstrapping for PHPUnit and beyond

Maintainers

👁 phalcon

Package info

github.com/phalcon/talon

pkg:composer/phalcon/talon

Fund package maintenance!

phalcon

Open Collective

Statistics

Installs: 117

Dependents: 1

Suggesters: 0

Stars: 0

Open Issues: 0

v0.2.0 2026-06-28 02:50 UTC

Requires

  • php: ^8.1

Suggests

  • ext-memcached: For the Services (Memcached) helpers
  • ext-phalcon: Phalcon C extension (^5) - one of ext-phalcon or phalcon/phalcon is required
  • phalcon/phalcon: Phalcon PHP implementation (^6) - alternative to the C extension
  • predis/predis: For the Services (Redis) helpers

Provides

None

Conflicts

None

Replaces

None

BSD-3-Clause 22396957f3ebf7d1abc69e61cb902c94d57c17b1

testingphpunittestphalconharness

This package is auto-updated.

Last update: 2026-06-28 03:10:08 UTC


README

👁 Latest Version
👁 PHP Version
👁 Total Downloads
👁 License

👁 Talon CI
👁 Quality Gate Status
👁 Coverage
👁 PDS Skeleton

👁 Discord
👁 Contributors
👁 OpenCollective Backers
👁 OpenCollective Sponsors

Test harness and Phalcon bootstrapping for PHPUnit and beyond - the part of Phalcon that catches the bugs.

Talon provides framework-neutral traits (the core), ready-to-extend PHPUnit base classes, and a one-liner bootstrap so any Phalcon project can write unit, integration, and functional tests with minimal boilerplate.

Requirements

  • PHP ^8.1
  • Phalcon - either the ext-phalcon C extension (^5) or the phalcon/phalcon PHP implementation (^6). Talon detects whichever is present.

Install

composer require --dev phalcon/talon

Bootstrap (one-liner)

// tests/bootstrap.php
require __DIR__ . '/../vendor/autoload.php';

use Phalcon\Talon\Settings;
use Phalcon\Talon\Talon;

Talon::boot(Settings::fromEnv());

Need setup hooks (the old loadIni / loadFolders)? Use the bootstrap runner:

use Phalcon\Talon\Bootstrap\Runner;
use Phalcon\Talon\Bootstrap\Stage;
use Phalcon\Talon\Settings;

Runner::for(Settings::fromArray(['root' => __DIR__ . '/..']))
 ->before(Stage::Environment, fn () => ini_set('memory_limit', '512M'))
 ->after(Stage::Directories, fn ($settings) => mkdir($settings->outputPath('screens'), 0777, true))
 ->boot();

Unit tests

use Phalcon\Talon\PHPUnit\AbstractUnitTestCase;

final class CalculatorTest extends AbstractUnitTestCase
{
 public function testInternal(): void
 {
 $this->assertSame(5, $this->callProtectedMethod(new Calculator(), 'add', 2, 3));
 }
}

AbstractUnitTestCase gives you callProtectedMethod(), getProtectedProperty(), setProtectedProperty(), invokeMethod(), getNewFileName(), safeDeleteFile(), safeDeleteDirectory(), assertFileContentsContains(), checkExtensionIsLoaded(), and checkPhalconAvailable().

Database tests

use Phalcon\Talon\PHPUnit\AbstractDatabaseTestCase;

final class UserTest extends AbstractDatabaseTestCase
{
 public function testSeeded(): void
 {
 $this->assertInDatabase('users', ['email' => 'nikos@niden.net']);
 }
}

The driver comes from the driver env (sqlite, mysql, pgsql); credentials come from Settings (env vars by default - see resources/.env.example).

Functional tests

The package never owns your container - hand it your configured application:

use Phalcon\Talon\PHPUnit\AbstractFunctionalTestCase;

final class HomeTest extends AbstractFunctionalTestCase
{
 protected function appFactory(): callable
 {
 return fn () => require __DIR__ . '/../app/bootstrap.php'; // returns a configured Application/Micro
 }

 public function testHome(): void
 {
 $this->dispatch('/');
 $this->assertController('index');
 $this->assertResponseContentContains('Welcome');
 }
}

Service tests (Redis / Memcached)

use Phalcon\Talon\PHPUnit\AbstractServicesTestCase;

final class CacheTest extends AbstractServicesTestCase
{
 public function testRedis(): void
 {
 $this->setRedisKey('key', 'value');
 $this->assertSame('value', $this->getRedisKey('key'));
 }
}

Service tests skip automatically when the backend is unreachable.

Mocking a Resultset (no database)

use Phalcon\Talon\Traits\ResultSetTrait;

final class ReportTest extends \PHPUnit\Framework\TestCase
{
 use ResultSetTrait;

 public function testReport(): void
 {
 $resultset = $this->mockResultSet([$modelA, $modelB]);
 $this->assertCount(2, $resultset);
 }
}

Custom configuration

Override getSettings() in a project base class, or pass Settings::fromArray([...]) to Talon::boot():

Talon::boot(Settings::fromArray([
 'root' => dirname(__DIR__),
 'db' => [
 'mysql' => ['host' => '127.0.0.1', 'port' => 3306, 'dbname' => 'app', 'username' => 'root', 'password' => ''],
 'sqlite' => ['dbname' => ':memory:'],
 ],
]));

Beyond PHPUnit

The traits are the core public API and carry no PHPUnit base-class requirement for their non-assertion helpers, so Pest (uses(...)) and other runners can consume them too. Pest and Codeception adapters are planned for a future release.

License

BSD-3-Clause. See LICENSE.