Retour à l'accueil

Framework Testo : tests PHP et benchmarks

Testo — framework indépendant pour les tests unitaires et benchmarks en PHP 8.2+. Prend en charge les plugins, tests inline dans src, Assert typé. Installation via Composer, exemples de code pour développeurs middle/senior.

Testo : nouveau framework de test pour PHP 8.2+
Advertisement 728x90

Testo : un framework PHP autonome pour les tests et les benchmarks

Le nouveau framework Testo est désormais ouvert aux tests bêta. Il s’agit d’une implémentation indépendante — sans dépendance à PHPUnit — compatible avec PHP 8.2+, dotée de plugins, de tests intégrés (inline tests) et de fonctionnalités de benchmarking. Conçu pour les développeurs PHP confirmés, Testo privilégie la flexibilité, le typage strict et la suppression du code répétitif.

Principales différences avec PHPUnit et ses alternatives

Testo a été conçu pour pallier les limites fondamentales de PHPUnit : manque de souplesse, ruptures fréquentes entre versions et stagnation au-delà du simple support des versions récentes de PHP. Son auteur a contribué au suivi des bogues et aux pull requests de PHPUnit — mais les améliorations proposées n’ont pas été intégrées. PEST et Codeception héritent des contraintes de PHPUnit tout en ajoutant une syntaxe parfois maladroite.

Testo est entièrement autonome :

Google AdInline article slot
  • Il évite nikic/php-parser, éliminant ainsi les conflits liés à l’analyse syntaxique.
  • Il requiert PHP 8.2 ou ultérieur.
  • Des agents IA peuvent générer automatiquement des tests à partir de llms.txt — une spécification structurée issue de la documentation officielle.

Un système de plugins modulaires permet une personnalisation par projet. Chaque fonctionnalité est livrée sous forme de plugin optionnel ; chaque suite de tests choisit ses propres extensions. Par exemple, le plugin #[Retry] ajoute une résilience face aux tests instables (flaky tests) — implémenté en seulement deux fichiers légers.

Étendez le comportement via des pipelines, des middleware et des événements. Écrivez vos tests directement dans votre code source (src/), avec l’attribut #[TestInline], et utilisez les attributs pour intégrer proprement la logique de test.

Installation et configuration

Installez Testo via Composer :

Google AdInline article slot
composer require --dev testo/testo

Créez un fichier testo.php à la racine de votre projet :

<?php

declare(strict_types=1);

use Testo\\Application\\Config\\ApplicationConfig;
use Testo\\Application\\Config\\SuiteConfig;

return new ApplicationConfig(
    suites: [
        new SuiteConfig(
            name: 'Sources',
            location: ['src'],
        ),
        new SuiteConfig(
            name: 'Tests',
            location: ['tests'],
        ),
    ],
);

Ce fichier renvoie une instance de ApplicationConfig. À défaut, Testo applique des valeurs par défaut (par exemple, analyse du dossier tests/). Deux suites sont préconfigurées : Sources, destinée aux tests intégrés et aux benchmarks insérés directement dans le code métier, et Tests, réservée aux tests unitaires classiques.

Exécutez les tests avec ./vendor/bin/testo — ou utilisez le plugin officiel PHPStorm pour une intégration transparente dans votre environnement de développement.

Google AdInline article slot

Exemples de tests unitaires

Aucune héritage requis. Annotez vos méthodes avec #[Test] :

final class OrderTest
{
    #[Test]
    public function calculatesTotal(): void
    {
        $order = new Order();
        $order->addItem('Livre', price: 15.0, quantity: 2);
        $order->addItem('Stylo', price: 3.0, quantity: 5);

        Assert::same($order->total(), 45.0);
    }

    #[Test]
    #[DataSet([100.0, 10, 90.0], '10 % de réduction')]
    #[DataSet([100.0, 0, 100.0], 'aucune réduction')]
    #[DataSet([0.0, 50, 0.0], 'prix nul')]
    public function appliesDiscount(float $price, int $percent, float $expected): void
    {
        $result = Order::applyDiscount($price, $percent);

        Assert::same($result, $expected);
    }

    #[Test]
    #[ExpectException(InsufficientFundsException::class)]
    public function cannotOverdraw(): never
    {
        new Account(balance: 100)->withdraw(200);
    }
}

Appliquez #[Test] à une classe pour traiter toutes ses méthodes void ou never comme des tests. Les fonctions autonomes (hors classes) s’exécutent sans attributs de cycle de vie.

Les assertions suivent un ordre intuitif : valeur réelle en premier, valeur attendue en second. Elles sont chaînables et pleinement typées :

Assert::string($email)->contains('@');

Assert::int($age)->greaterThan(0)->lessThan(150);

Assert::array($items)
    ->hasKeys('id', 'nom')
    ->isList()
    ->notEmpty();

Assert::json($response->body())
    ->isObject()
    ->hasKeys('données', 'métadonnées');

Tests intégrés et benchmarks

Utilisez #[TestInline] dans src/ pour valider des méthodes privées ou internes :

// src/Money.php
final class Money
{
    #[TestInline(['prix' => 100.0, 'remise' => 0.1, 'taxe' => 0.2], 108.0)]
    #[TestInline(['prix' => 50.0, 'remise' => 0.0, 'taxe' => 0.1], 55.0)]
    private static function calculateFinalPrice(
        float $price,
        float $discount,
        float $tax,
    ): float {
        return $price * (1 - $discount) * (1 + $tax);
    }
}

Mesurez les performances avec #[Bench] :

#[Bench(
    callables: [
        'multiply' => 'viaMultiply',
        'shift'    => 'viaShift',
    ],
    arguments: [1, 5_000],
    calls: 2_000_000,
)]
function viaDivision(int $a, int $b): int
{
    $d = $b - $a + 1;
    return (int) (($d - 1) * $d / 2) + $a * $d;
}

function viaMultiply(int $a, int $b): int
{
    $d = $b - $a + 1;
    return (int) (($d - 1) * $d * 0.5) + $a * $d;
}

function viaShift(int $a, int $b): int
{
    $d = $b - $a + 1;
    return ((($d - 1) * $d) >> 1) + $a * $d;
}

Résultat type :

+---+----------+------------+---------+------------------+--------+
| # | Nom      | Itérations | Appels  | Temps moyen      | Écart  |
+---+----------+------------+---------+------------------+--------+
| 2 | actuel   | 10         | 2000000 | 75 890 µs        | ±0,79% |
| 3 | multiply | 10         | 2000000 | 78 821 µs (+3,9%)| ±0,47% |
| 1 | shift    | 10         | 2000000 | 70 559 µs (-7,0%)| ±0,70% |
+---+----------+------------+---------+------------------+--------+

Pourquoi cela compte

  • Un framework véritablement autonome — zéro dépendance à PHPUnit, extensible via des plugins composables.
  • Tests intégrés (#[TestInline]) et benchmarks (#[Bench]) directement intégrés au code métier dans src/.
  • Chaînes d’assertions typées et fluides — zéro code répétitif, zéro héritage, zéro variable globale magique.
  • Configuration via des fichiers PHP simples, avec prise en charge native des projets multi-suites.
  • Plugin PHPStorm et outils CLI de première qualité — prêts pour une utilisation quotidienne.

— Editorial Team

Advertisement 728x90

Lire ensuite