testo / testo
A lightweight PHP testing framework.
Package info
pkg:composer/testo/testo
Fund package maintenance!
Requires
- php: >=8.2
- ext-tokenizer: *
- internal/destroy: ^1.0
- internal/path: ^1.2
- psr/container: 1 - 2
- psr/event-dispatcher: ^1.0
- psr/log: ^2.0 || ^3.0
- symfony/console: ^6.4 || ^7 || ^8.0
- symfony/finder: ^6.4 || ^7 || ^8.0
- testo/assert: ^0.1.12
- testo/bench: ^0.1.8
- testo/bridge-symfony-console: ^0.1.8
- testo/codecov: ^0.1.12
- testo/convention: ^0.1.4
- testo/data: ^0.1.7
- testo/fiber: ^0.1.2
- testo/filter: ^0.1.6
- testo/inline: ^0.1.8
- testo/lifecycle: ^0.1.5
- testo/repeat: ^0.1.9
- testo/retry: ^0.1.5
- testo/test: ^0.1.6
- yiisoft/injector: ^1.2
Requires (Dev)
- bamarni/composer-bin-plugin: ^1.8
- buggregator/trap: ^1.10
- internal/dload: ^1.6
- llm/skills: ^1.3
- php-vcr/php-vcr: ^1.6
- rector/rector: ^2.5.2
- roxblnfk/unpoly: 1.8.2
- testo/bridge-infection: ^0.1.8
- testo/bridge-mockery: ^0.1.2
- testo/bridge-rector: ^0.2.2
- testo/bridge-revolt: ^0.1.1
- testo/bridge-vcr: ^0.1.0
- testo/facade: ^0.1.1
Suggests
- llm/skills: Ship Testo's bundled AI-agent skills into your project's skills directory automatically.
- testo/bridge-infection: Provides integration with Infection PHP mutation testing framework.
- testo/bridge-mockery: Provides integration with the Mockery mocking framework.
Replaces
- internal/container: *
- internal/fiber: *
This package is auto-updated.
Last update: 2026-08-10 12:39:29 UTC
README
The PHP Testing Framework You Control
Testo is an extensible testing framework built on a lightweight core with a middleware system. It gives you full control over your testing environment while keeping the familiar PHP syntax you already know.
Get Started
Installation
composer require --dev testo/testo *
Configuration
The fastest way to set up Testo in your project is the built-in init command:
vendor/bin/testo init
It will:
- detect your
src/directory (or prompt for it), - create
tests/Unit/if missing, - generate a minimal
testo.phpnext to yourcomposer.json, - register
composer testandcomposer test:<suite>scripts.
For a sub-app layout, point it at the project root: vendor/bin/testo init --path=app.
Tuning testo.php manually
testo.php is plain PHP returning an ApplicationConfig — edit it freely to add suites, plugins, or coverage. A typical setup looks like:
<?php declare(strict_types=1); use Testo\Application\Config\ApplicationConfig; use Testo\Application\Config\Plugin\SuitePlugins; use Testo\Application\Config\SuiteConfig; use Testo\Bench\BenchmarkPlugin; use Testo\Inline\InlineTestPlugin; return new ApplicationConfig( src: ['src'], suites: [ new SuiteConfig( name: 'Sources', location: ['src'], // Only Benchmarking and Inline Tests for Source files plugins: SuitePlugins::only( new InlineTestPlugin(), new BenchmarkPlugin(), ), ), new SuiteConfig( name: 'Unit', location: ['tests/Unit'], ), ], );
If no testo.php is present at all, Testo falls back to running every test under the tests/ folder.
Running Tests
To run your tests, execute:
composer test
…or call the binary directly if you skipped init / didn't register the script:
vendor/bin/testo
You can also run a single suite by name (one script per detected suite is registered by init):
composer test:unit composer test:integration
Writing Your First Test
Create a test class in the configured test directory (e.g., tests/CalculatorTest.php):
<?php declare(strict_types=1); namespace Tests; use Testo\Assert; use Testo\Assert\ExpectException; use Testo\Retry; use Testo\Test; #[Test] final class CalculatorTest { public function dividesNumbers(): void { $result = 10 / 2; Assert::same($result, 5.0); Assert::notSame($result, 5); // Types matter! } #[Retry(maxAttempts: 3)] public function flakyApiCall(): void { // Retries up to 3 times if test fails $response = $this->makeExternalApiCall(); Assert::same($response->status, 200); } #[ExpectException(\RuntimeException::class)] public function throwsException(): void { throw new \RuntimeException('Expected error'); } }
What to note:
- Use the
#[Test]attribute to mark test methods or classes - Test classes don't need to extend any base class
- Use
Assertclass for assertions (same,true,false,null,contains,instanceOf, etc.) - Testo provides multiple attributes to extend testing capabilities (retry policies, exception handling, and more)
IDE Support
Testo comes with the IDEA plugin Testo.