rawsrc / exacodis
一个非常简约的PHP测试引擎
Requires
- php: >=8.0
This package is auto-updated.
Last update: 2024-09-27 09:31:15 UTC
README
2022-08-15 PHP 8.0+ v.1.2.3
PHP 测试引擎
Exacodis 是一个针对 PHP 的非常简约的测试引擎(非常轻量级的 PHP 测试框架)。这个引擎与其他工具相比非常简单易用。没有复杂的架构,甚至不是一个庞大的测试引擎,只是基础(以及一点点)来帮助您验证您的 PHP 代码。
只有 3 个类
- 一个用于控制测试(称为
Pilot), - 一个用于封装和执行测试代码(称为
Runner) - 一个用于生成报告(称为
Report)
以及许多辅助函数来检查返回值与预期类型和/或值的匹配。辅助函数并非详尽无遗,您将能够轻松创建自己的。
安装
composer require rawsrc/exacodis
重要
请注意,您的测试源代码必须完全干净:您不能覆盖测试运行、结果或资源。
如果您这样做,则代码将失败并抛出 Exception,直到您修复它。
变更日志
- 添加了测试任何受保护/私有方法的可能
- 添加了测试任何受保护/私有静态方法的可能
- 与上一个版本兼容
如何使用
我将直接使用 Exacodis 来测试它自己,作为学习支持。
由于 Exacodis 是一个非常轻量级的引擎,您需要自行设置测试环境。显然,这就像
<?php declare(strict_types=1); include 'Pilot.php'; use Exacodis\Pilot;
对于拥有许多类的项目,您必须告诉 PHP 如何加载您的类,无论是通过包含它们还是使用自动加载器。
这就足够开始测试您的代码了。
概念
Pilot类做所有事情。您不必使用另外两个类;Runner和Report。- 所有测试代码都必须封装在一个返回值的
Closure中。 - 您可以创建的辅助函数用于引擎的
assert部分。您可以根据需要创建尽可能多的断言。标准库中已经包含了大量的辅助函数。
开始吧
// create new project $pilot = new Pilot('Exacodis - A PHP minimalist test framework'); $pilot->injectStandardHelpers();
- 资源
为了使用所有测试代码所需的功能,引擎能够存储和检索您想要的任何资源(对象、数组、标量值,甚至是单元测试...)。每个资源必须有一个唯一的名称,并且您不能意外地覆盖它。
//region resources $pilot->addResource('year', 2021); $pilot->addResource('years', [2020, 2021]); $pilot->addResource('is_leap', false); $pilot->addResource('current_month', 'september'); $pilot->addResource('dummy_array_data', [ $pilot->getResource('year'), $pilot->getResource('is_leap'), $pilot->getResource('current_month') ]); //endregion
- 测试
如上所述,测试是一段简单的代码片段,封装在返回值的 Closure 中
$pilot->run( id: '001', description: 'Resource extractor (integer) - assertIsInt assertEqual assertNotEqual assertIn assertInStrict', test: fn() => $pilot->getResource('year') );
- 断言
断言使用标准辅助函数和当然也是您的。
$pilot->assertIsInt(); $pilot->assertEqual(2021); $pilot->assertNotEqual(2000); $pilot->assertIn(['2021']); $pilot->assertInStrict([2021]);
您必须知道,断言(->assertXXX)始终应用于最新的一次运行。如果您想更改当前运行器,则可以请求它
$pilot->setCurrentRunnerTo('001');
然后断言将应用于此(见下文:嵌套运行)
- 动态断言
这是编写动态测试的方法
$pilot->assert( test: fn() => count($pilot->getRunner('select_001')->getResult()) === 2, test_name: 'Count the records', expected: 2, );
- 复杂的测试代码
您可以将测试代码作为原始代码编写,特别是对于复杂的测试代码。
// manual test $stats = $pilot->getStats(); unset($stats['milliseconds'], $stats['hms']); // we encapsulate the result in a closure to use it for testing purpose $pilot->run( id: '007', description: 'check the count', test: fn() => $stats; ); // then we lead our assertions $pilot->assertIsArray(); $pilot->assertEqual([ 'nb_runs' => 6, 'passed_runs' => 5, 'failed_runs' => 1, 'passed_runs_percent' => round(5/6*100, 2), 'failed_runs_percent' => 100-round(5/6*100, 2), 'nb_assertions' => 18, 'passed_assertions' => 17, 'failed_assertions' => 1, 'passed_assertions_percent' => round(17/18*100, 2), 'failed_assertions_percent' => 100-round(17/18*100, 2) ]);
- 测试类中的受保护/私有和/或静态方法
为了能够测试任何 protected 或 private(static 或不)方法,您必须使用 $pilot->runClassMethod(...) 而不是 $pilot->run(...)。方法的签名是
public function runClassMethod( int|string|null $id, object|string $class, string $description = '', ?string $method = null, array $params = [], )
请注意
- 如果类有一个复杂的构造函数且需要参数,那么您必须为
$class提供一个干净的对象实例。 - 在其他情况下,
$class可以是一个字符串,例如Foo或者甚至是Foo::method。这对于没有构造函数或者构造函数没有必需参数的类同样适用。 - 数组
$params必须包含调用方法所需的所有参数。它也支持命名参数。
其他部分与 $pilot->run() 方法类似。
以下是一个来自 PHP 测试文件的例子:这里所有的测试都是等效的。
$foo = new Foo(); $pilot->runClassMethod( id: '008', description: 'private method unit test using directly an instance of Foo', class: $foo, // instance method: 'abc', ); $pilot->assertIsString(); $pilot->assertEqual('abc'); $pilot->runClassMethod( id: '009', description: 'private method unit test using string notation for the class Foo', class: 'Foo', // class name method: 'abc', ); $pilot->assertIsString(); $pilot->assertEqual('abc'); $pilot->runClassMethod( id: '010', description: 'private method unit test using short string notation for the class Foo and the method abc', class: 'Foo::abc', // short notation ); $pilot->assertIsString(); $pilot->assertEqual('abc');
看看如何使用两个参数调用私有方法,以及另一个调用 private static function() 的例子。
$pilot->runClassMethod( id: '012', description: 'private method unit test with two parameters', class: 'Foo', method: 'hij', params: ['p' => 25, 'q' => 50] // or [25, 50] ); $pilot->assertIsInt(); $pilot->assertEqual(250); $pilot->runClassMethod( id: '018', description: 'private static method unit test', class: 'Foo::tuv', ); $pilot->assertIsString(); $pilot->assertEqual('tuv');
命名参数必须按照函数原型中定义的顺序排列。
- 报告
引擎内部计算数据,你可以简单地请求一个 HTML 报告,就像这样。
$pilot->createReport();
在存储库中你可以找到两个报告:一个完全通过,另一个失败。你可以确切地看到引擎是如何跟踪执行过程以及保持了哪些数据。
- 助手
你可以创建自己的助手来验证任何结果,使用简单的 Closure。请查看
//region equal $equal = function(mixed $to): void { /** @var Pilot $this */ if ($this->current_runner->getResult() === $to) { $this->addSuccess('equal'); } else { $this->addFailure(expected: 'Equal to: '.print_r($to, true)); } }; $helpers['assertEqual'] = $equal; //endregion
这个断言是标准库的一部分,并且在项目开始后立即注入。你也可以使用 $pilot->addHelper($name, $closure); 动态定义一个助手。
- 嵌套运行
对于真正复杂的测试,你还可以定义嵌套运行。
$pilot->run( id: 'abc', description: 'complex nested tests', test: function() use ($pilot) { // nested run $pilot->run( id: 'def', description: 'nested run', test: function() { // ... return $foo; } ) // careful: this applies to the (latest) run which is here 'def' $pilot->assertIsArray(); return []; // a run MUST always return a value } ); // careful, if you continue the assertions here, by default they will apply to the // (latest) run which is still 'def'; you must change the current runner to work with the previous one $pilot->setCurrentRunnerTo('abc'); $pilot->assertIsArray(); // now it applies to the run 'abc'
这是 Exacodis 的唯一难点。这使代码更易于阅读,因为不需要在每个函数调用中设置大量的参数。
祝您玩得开心!
rawsrc