返回首页

Testo 框架:PHP 测试和基准测试

Testo — PHP 8.2+ 的单元测试和基准测试独立框架。支持插件、src 中的内联测试、类型化 Assert。通过 Composer 安装,针对中高级开发者的代码示例。

Testo:适用于 PHP 8.2+ 的全新测试框架
Advertisement 728x90

Testo:一款独立的 PHP 测试与性能基准测试框架

全新 Testo 框架现已开放公测。它是一个零依赖实现——完全不依赖 PHPUnit——支持 PHP 8.2+、插件机制、内联测试及性能基准测试。专为中高级 PHP 开发者设计,Testo 始终坚持灵活性、严格类型约束与极简样板代码。

与 PHPUnit 及其他方案的核心差异

Testo 的诞生,旨在解决 PHPUnit 长期存在的核心痛点:架构僵化、频繁破坏性更新,以及在基础 PHP 版本兼容之外缺乏实质性演进。作者曾长期向 PHPUnit 官方问题追踪器提交建议并贡献 PR,但关键改进始终未被接纳。而 PEST 和 Codeception 实质上继承了 PHPUnit 的全部局限,仅以更繁琐的语法加以包装。

Testo 彻底独立自主:

Google AdInline article slot
  • 完全摒弃 nikic/php-parser,从根源上规避解析器冲突;
  • 要求 PHP 8.2+ 运行环境;
  • 支持 AI 代理通过 llms.txt(一份源自官方文档的结构化规范)自动批量生成测试用例。

模块化插件系统支持按项目定制功能:所有特性均以可选插件形式交付,每个测试套件可自由组合所需能力。例如,#[Retry] 插件仅用两个轻量文件即可赋予测试用例应对偶发失败的韧性。

通过管道(pipeline)、中间件(middleware)与事件(event)机制灵活扩展行为;直接在 src/ 目录源码中使用 #[TestInline] 编写内联测试,并借助属性(attribute)优雅嵌入测试逻辑。

安装与配置

通过 Composer 安装:

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

在项目根目录创建 testo.php

<?php

declare(strict_types=1);

use Testo<Application>Config<ApplicationConfig>;
use Testo<Application>Config<SuiteConfig>;

return new ApplicationConfig(
    suites: [
        new SuiteConfig(
            name: '源码',
            location: ['src'],
        ),
        new SuiteConfig(
            name: '测试',
            location: ['tests'],
        ),
    ],
);

该文件需返回一个 ApplicationConfig 实例。若缺失此文件,Testo 将自动回退至默认配置(如扫描 tests/ 目录)。默认预置两套测试集:源码 用于运行嵌入生产代码中的内联测试与性能基准测试;测试 则承载传统单元测试。

执行测试命令为 ./vendor/bin/testo;亦可安装官方 PHPStorm 插件,实现无缝 IDE 集成。

Google AdInline article slot

单元测试示例

无需继承任何基类。只需为方法添加 #[Test] 属性:

final class OrderTest
{
    #[Test]
    public function 计算订单总额(): void
    {
        $order = new Order();
        $order->addItem('图书', price: 15.0, quantity: 2);
        $order->addItem('钢笔', price: 3.0, quantity: 5);

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

    #[Test]
    #[DataSet([100.0, 10, 90.0], '享 10% 折扣')]
    #[DataSet([100.0, 0, 100.0], '无折扣')]
    #[DataSet([0.0, 50, 0.0], '价格为零')]
    public function 应用折扣(float $price, int $percent, float $expected): void
    {
        $result = Order::applyDiscount($price, $percent);

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

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

对整个类添加 #[Test] 属性后,其所有返回 voidnever 的方法将自动视为测试用例。独立函数(非类内定义)则无需生命周期属性即可直接运行。

断言采用直观顺序:实际值在前,期望值在后。所有断言均支持链式调用且具备类型安全:

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

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

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

Assert::json($response->body())
    ->isObject()
    ->hasKeys('data', 'meta');

内联测试与性能基准测试

src/ 目录中使用 #[TestInline],可直接验证私有或内部方法:

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

使用 #[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;
}

典型输出:

+---+----------+-------+---------+------------------+--------+
| # | 名称     | 迭代数 | 调用次数 | 平均耗时         | 标准差 |
+---+----------+-------+---------+------------------+--------+
| 2 | 当前实现 | 10    | 2000000 | 75.890µs         | ±0.79% |
| 3 | 乘法运算 | 10    | 2000000 | 78.821µs (+3.9%) | ±0.47% |
| 1 | 位移运算 | 10    | 2000000 | 70.559µs (-7.0%) | ±0.70% |
+---+----------+-------+---------+------------------+--------+

为何值得关注

  • 真正意义上的独立框架——零 PHPUnit 依赖,通过可组合插件实现无限扩展;
  • 内联测试(#[TestInline])与性能基准测试(#[Bench])直接与生产代码共存于 src/ 目录,保障逻辑与验证同步演进;
  • 类型安全、流畅的断言链式调用——告别样板代码、继承束缚与魔法全局变量;
  • 配置采用纯 PHP 文件,天然支持多测试套件复杂项目;
  • 官方 PHPStorm 插件与 CLI 工具开箱即用,深度融入日常开发流程。

— Editorial Team

Advertisement 728x90

继续阅读