lucinda/ unit-testing
轻量级的PHP8.1单元测试库,零依赖
Requires
- php: ^8.1
- ext-curl: *
- ext-pdo: *
- ext-simplexml: *
- ext-tokenizer: *
- lucinda/console: ~2.0
This package is auto-updated.
Last update: 2024-09-05 11:32:07 UTC
README
目录
关于
这个库部分是在使用PHPUnit时感到沮丧而创建的,PHPUnit是超过99%的具有单元测试功能的PHP应用程序的标准解决方案。这个软件的目标是构建PHPUnit不具备的东西:一个干净编码、零依赖的API!
开发人员只需遵循以下步骤
- 配置:设置一个XML文件,其中配置了单元测试
- 初始化:为测试的目标API自动创建单元测试架构(类和方法)
- 开发:为上面创建的每个类方法开发一个或多个单元测试
- 执行:在上述基础上自动执行单元测试,并在控制台或JSON中显示单元测试结果
API完全符合PSR-4规范,仅需要PHP8.1+解释器,SimpleXML + cURL + PDO扩展(后者用于URI和SQL测试)和Console Table API(用于显示单元测试结果)。要快速了解其工作原理,请查看
- 安装:描述了如何在计算机上安装API,考虑到上述步骤
- 断言:描述了如何使用此API进行断言
- 示例:显示了OAuth2客户端API的真实单元测试
为什么不使用PHPUnit
关于PHPUnit的所有事情都让人联想到过去的时代,那时开发者构建了庞大的类,这些类可以做“一切”,除了“extends”关键字外,对封装一无所知(怀疑者应该检查https://github.com/sebastianbergmann/phpunit/blob/master/src/Framework/TestCase.php所有PHPUnit测试都必须扩展!)。
是否可以做更好的事情?单元测试API是否应遵循面向对象编程的良好原则,或者只是被测试的代码?我认为,只要开发者感觉在混乱中工作很舒服,那么在以后构建类似的东西就会成为不良先例。必须做更好的事情!
配置
类似于PHPUnit,单元测试的配置是通过一个XML文件完成的,其语法如下
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE xml> <xml> <unit_tests> <unit_test> <sources path="PATH" namespace="NAMESPACE"/> <tests path="PATH" namespace="NAMESPACE"/> </unit_test> ... </unit_tests> <servers> <sql> <ENVIRONMENT> <server driver="DRIVER" host="HOSTNAME" port="PORT" username="USERNAME" password="PASSWORD" schema="SCHEMA" charset="CHARSET"/> </ENVIRONMENT> ... </sql> </servers> </xml>
必选标签unit_tests存储要执行的单元测试套件。每个测试的API由一个具有以下子标签的unit_test标签标识:
- sources:通过同名属性配置源的基本路径path(例如:src)和基本APInamespace(例如:Lucinda\Logging)
- 测试:通过同名属性配置测试的基础路径 path(例如:tests)和基础API 命名空间(例如:Test\Lucinda\Logging)
这些设置将用于在需要时自动加载 test/sources 类,例如在 composer 的情况下,您必须使用完全限定的命名空间,并以反斜杠结尾。
可选标签 服务器 存储单元测试中将要使用的 SQL 服务器连接设置,按 ENVIRONMENT(通过 php getenv("ENVIRONMENT")
的值)分隔。每个服务器必须映射到具有以下属性的 服务器 子标签,以配置连接
- DRIVER:(必填)PDO 识别的 SQL 供应商名称。例如:mysql
- HOSTNAME:(必填)当前数据库服务器主机名。例如:127.0.0.1
- PORT:(可选)当前数据库服务器端口号。例如:3306
- USERNAME:(必填)数据库服务器用户名。例如:root
- PASSWORD:(必填)数据库服务器用户密码。例如:my-password
- SCHEMA:(可选)单元测试将运行的架构名称。例如:test_schema
- CHARSET:(可选)连接中使用的默认字符集。例如:utf8
示例:在 单元测试 @ OAuth2 客户端 API
实现
初始化
只需运行一个 Lucinda\UnitTest\Controller 实现(参见 安装 部分),sources 文件夹中的类就会根据以下规则镜像到 tests 文件夹
- 保留原始文件夹结构,只是类名被重命名(见下文)
- 保留原始类和文件名,只是后面添加了 "Test"。因此,MyClass 和 MyClass.php 镜像到 MyClassTest 和 MyClassTest.php
- 保留原始命名空间,只是前面添加了 "Test" 命名空间。因此,Foo\Bar 镜像到 Test\Foo\Bar
- 只镜像源类的公共方法
- 忽略源方法的参数和返回类型,因此原始 asd(string fgh): int 将镜像到 php asd()
- 所有创建的方法都将具有空体
这确保了每次执行时都保持 100% 的覆盖率,让程序员自己开发缺失的单元测试
开发
为了被覆盖,每个 tests 类的公共方法必须返回一个 Lucinda\UnitTest\Result 实例或一个实例列表,具体取决于您是否希望进行一个或多个测试。每个测试都有一个状态(通过或不通过)和一个可选的消息(包含标识测试的细节)。
示例
namespace Test\Foo; // mirrors source namespace: Foo class BarTest { // mirrors class: Bar public function asd(): Lucinda\UnitTest\Result // mirrors method: asd @ Bar { $object = new \Foo\Bar(...); $data = $object->asd(...); // makes a single numeric assertion return (new Lucinda\UnitTest\Validator\Integers($data))->assertEquals(12); } public function fgh(): array // mirrors method: fgh @ Bar { $results = []; $object = new \Foo\Bar(...); $data = $object->fgh(...); // makes multiple assertions on same value $test = new Lucinda\UnitTest\Validator\Arrays($data); $results[] = $test->assertNotEmpty("is it empty"); $results[] = $test->assertContainsValue("qwerty"); return $results; } }
执行
只需运行一个 Lucinda\UnitTest\Controller(参见 安装 部分),tests 文件夹中的类就会被实例化,它们的公共方法会按设置的顺序执行,并收集 Lucinda\UnitTest\Result 实例。逻辑如下:
- 如果类 @ src 没有镜像类 @ tests,则对相应类的单元测试标记为失败!
- 如果类 @ src 中有公共方法不在镜像类 @ tests 中,则对相应方法的单元测试标记为失败!
- 如果镜像类的任何方法不返回Lucinda\UnitTest\Result 或其列表,单元测试将标记为失败,并显示消息表示该方法未覆盖
- 单元测试的结果被收集到Lucinda\UnitTest\Result 的列表中
这个抽象类包含以下感兴趣的方法
API 已经包含两个Lucinda\UnitTest\Controller 实现
- Lucinda\UnitTest\ConsoleController:在控制台上以表格形式显示单元测试结果
- Lucinda\UnitTest\JsonController:以 JSON 格式显示单元测试结果
开发者可以构建自己的扩展,并将结果保存到某个位置...
安装
在您的 API 所在的文件夹中,请在控制台运行以下命令
composer require lucinda/unit-testing
然后创建一个包含配置设置(参见上文的配置)的 unit-tests.xml 文件和一个包含以下代码的 test.php 文件
require(__DIR__."/vendor/autoload.php"); try { new Lucinda\UnitTest\ConsoleController("unit-tests.xml", "local"); } catch (Exception $e) { // handle exceptions }
要查看使用示例,请查看单元测试,这些测试针对OAuth2 客户端 API!
断言
原始值断言
API 允许您对所有的 PHP 原始数据类型进行断言
- 整数:通过 Lucinda\UnitTest\Validator\Integers
- 浮点数:通过 Lucinda\UnitTest\Validator\Floats
- 字符串:通过 Lucinda\UnitTest\Validator\Strings
- 布尔值:通过 Lucinda\UnitTest\Validator\Booleans
- 数组:通过 Lucinda\UnitTest\Validator\Arrays
- 对象:通过 Lucinda\UnitTest\Validator\Objects
这些类中的每一个都有一个构造函数,其中注入了相应类型的值,然后是一系列对该值进行断言的方法。在现实生活中,您将只使用这些类进行单个断言。
断言示例
$test = new Lucinda\UnitTest\Validator\Arrays($data); return $test->assertNotEmpty("is it empty");
SQL 查询结果断言
有时需要测试数据库中的信息。为此,您可以使用 API 提供的 Lucinda\UnitTest\Validator\SQL 类,该类有四个公共方法
断言示例
$test = new Lucinda\UnitTest\Validator\SQL($dataSource); $test->assertStatement("SELECT COUNT(id) AS nr FROM users", new class extends Lucinda\UnitTest\Validator\SQL\ResultValidator() { public function validate(\PDOStatement $statementResults): Result { $test = new Lucinda\UnitTest\Validator\Integer((integer) $statementResults->fetchColumn()); return $test->assertEquals(8); } });
上述机制允许您对单个 Lucinda\UnitTest\Validator\SQL 实例开发多个断言,这又对应于单个 SQL 连接。
URL 执行结果断言
有时需要测试 URL 执行的结果。为此,您可以使用 API 提供的 Lucinda\UnitTest\Validator\URL 类,该类有两个公共方法
断言示例
$test = new Lucinda\UnitTest\Validator\URL(new Lucinda\UnitTest\Validator\URL\DataSource("https://www.google.com")); $test->assert(new class extends Lucinda\UnitTest\Validator\URL\ResultValidator() { public function validate(Lucinda\UnitTest\Validator\URL\Response $response): Result { $test = new Lucinda\UnitTest\Validator\Strings($response->getBody()); return $test->assertContains("google"); } });
上述机制允许您通过单个 Lucinda\UnitTest\Validator\URL 实例对相同的 URL 执行结果开发多个断言。
文件断言
可以通过使用Lucinda\UnitTest\Validator\Files类对文件进行断言,该类提供了以下公共方法:
断言示例
$test = new Lucinda\UnitTest\Validator\Files("foo/bar.php"); return $test->assertExists();
示例
此OAuth2 客户端 API是使用此API进行单元测试的API之一,因此请检查:
- unit-tests.xml:配置示例
- test.php:测试套件执行器示例
- tests:来自src的类单元测试示例
- tests_drivers:来自drivers的类单元测试示例