mindplay/testies

是的,testies:一个轻量级的库,用于快速、简单的单元测试

1.1.5 2024-05-21 17:08 UTC

README

PHP Version

是的,testies:一个轻量级函数库,用于快速、简单的单元测试。

试图尊重Go语言测试哲学 - 概括

测试框架往往会发展成自己的迷你语言,拥有条件语句、控制机制和打印机制,但PHP已经拥有所有这些功能;为什么还要重新创建它们?我们更愿意用PHP编写测试;这样就可以少学习一门语言,并且这种方法使测试更加简单易懂。

主要的测试API是一组位于mindplay\testies命名空间中的函数。

内部,API由一对简单的“驱动”和配置类支持 - 这些类尽可能开放,你应该感到舒适地扩展这些类,并针对你正在测试的内容的测试需求进行定制。

使用方法

通过Composer安装

composer require-dev mindplay/testies

然后创建一个测试脚本 - 格式非常简单

<?php

// import the functions you need:

use function mindplay\testies\{test, ok};

// bootstrap Composer:

require dirname(__DIR__) . '/vendor/autoload.php';

// define your tests:

test(
    'Describe your test here',
    function () {
        ok(true, 'it works!');
    }
);

// run your tests:

exit(run()); // exits with errorlevel (for CI tools etc.)

你可以根据需要多次调用test() - 测试将在调用run()时排队执行。

API

以下函数可在mindplay\testies命名空间中使用

# Assertions:

ok($result, $why, $value);                 # check the result on an expression
eq($value, $expected, $why);               # check value for strict equality with an expected value
expect($exception_type, $why, $function);  # check for an expected exception that should be thrown 

# Helper functions:

invoke($object, $method_name, $arguments); # invokes a protected or private method; returns the result
inspect($object, $property_name);          # returns a protected or private property value
format($value, $detailed = false);         # format a value for use in diagnostic messages

而不是提供数百个断言函数,您可以使用PHP表达式执行断言,通常与您自己的辅助函数或PHP内置标准函数一起使用 - 一些示例

test(
    'Various things of great importance',
    function () {
        ok($foo instanceof Foo);              # type-checking an object
        ok(is_int(inspect($foo, '_count')));  # type-checking a private property
        ok(123 == '123');                     # loose comparison
        ok(in_array('b', ['a','b','c']));     # check for presence of a value
        ok(isset($map['key']));               # check for presence of a key
        ok(is_string(@$map['key']));          # type-check a key/value with error-suppression
    }
);

我们发现,惯用的PHP代码是您已经知道的东西 - 我们相信编写更接近普通日常代码的测试,而不是为测试创建自己的领域特定语言。

自定义断言函数

您的自定义断言函数,就像内置断言函数一样,只是函数 - 通常这些函数会回调到ok()函数以报告结果。例如

/**
 * Assert that a numeric value is very close to a given expected value 
 * 
 * @param float|int $value    actual value
 * @param float|int $expected expected near value
 * @param int       $decimals number of decimals of error tolerance
 */
function nearly($value, $expected, $decimals = 8) {
    ok(abs($value - $expected) * pow(10, $decimals) <= 1, "{$value} should be nearly {$expected}", $value);
}

test(
    'Values should be approximately right',
    function () {
        nearly(9.999999999, 10);
        nearly(10.000000001, 10);
        nearly(10.00002, 10);
    }
);

您可以使用相同的方法对多个断言进行分组以供重用

function checkValue($value) {
    ok(is_int($value), "value should be numeric", $value);
    ok($value > 0, "value should be positive", $value);
}

test(
    'Checking out some numbers',
    function () {
        checkValue(123);
        checkValue(-1);
    }
);

请注意,诊断输出将始终引用生成断言结果的测试闭包中的行号。

测试服务器

⚠️ 此功能尚处于草稿阶段。

PHP提供了一个内置的开发Web服务器

对于基本的集成测试,提供了一个简单的包装类来启动和关闭服务器 - 下面的示例使用了nyholm/psr7zaphyr-org/http-client客户端库

use Nyholm\Psr7\Factory\Psr17Factory;
use Zaphyr\HttpClient\Client;
use function mindplay\testies\{test, ok, eq};

$server = new TestServer(__DIR__, 8088);

test(
    'Can get home page',
    function () {
        $server = new TestServer(__DIR__, 8088);

        $http = new Psr17Factory();

        $client = new Client($http, $http);

        $response = $client->sendRequest($http->createRequest("GET", "http://127.0.0.1:8088/index.php"));

        eq($response->getStatusCode(), 200);

        ok(strpos($response->getBody(), '<title>Welcome</title>') !== false, "it should capture the response body");
    }
);

请注意,当$server对象超出作用域时,服务器将自动关闭 - 如果您需要显式关闭服务器,只需使用例如unset($server)销毁服务器对象即可。

请记住,启动和停止多个服务器实例可能会极大地减慢您的测试速度 - 通常,打开一个服务器实例并在测试函数之间共享它是很好的主意。另一方面,创建和销毁客户端是推荐的,因为共享客户端状态可能会导致不可靠的测试。

选项

通过configure()函数提供了一些简单的配置选项,该函数提供了对当前TestConfiguration实例的访问。

代码覆盖率

要启用代码覆盖率并在控制台显示摘要结果

configure()->enableCodeCoverage();

要输出clover.xml文件以便与外部分析工具集成,请指定输出路径

configure()->enableCodeCoverage(__DIR__ . '/build/clover.xml');

为了仅对特定文件夹中的文件启用代码覆盖率分析,请传入一个路径(或路径数组)如下

configure()->enableCodeCoverage(__DIR__ . '/build/clover.xml', dirname(__DIR__) . '/src');

详细程度

默认情况下,测试输出不会产生关于成功断言的消息,只有失败才会显示 - 如果您想查看更多内容,请启用详细输出

configure()->enableVerboseOutput();

您也可以通过命令行使用 -v--verbose 开关来启用此功能。

严格错误处理

默认情况下,所有PHP错误/警告/通知都会通过内置的错误处理器自动映射到异常。如果您正在测试具有自定义错误处理的内容,您可以使用以下方法来禁用它

configure()->disableErrorHandler();

可扩展性

过程式API实际上是在两个提供实际库实现的类之上的一个薄层。

使用自定义驱动器的一个常见原因是覆盖 TestDriver::format() 方法,以自定义在控制台上输出特殊对象的格式。

要使用自定义的派生 TestConfiguration

// Derive your custom configuration class:

class MyTestConfiguration extends TestConfiguration
{
    // ...
}

// Head off your test by selecting your custom configuration object:

configure(new MyTestConfiguration);

然后按照常规业务流程进行。

要使用自定义的派生 TestDriver

// Derive your custom driver class:

class MyTestDriver extends TestDriver
{
    // ...
}

// Boostrap your test by selecting your custom driver:

configure(new TestConfiguration(new TestDriver));

或者,创建一个配置类,该类提供了一个自定义默认驱动器类

class MyTestDriver extends TestDriver
{
    // ...
}

class MyTestConfiguration extends TestConfiguration
{
    protected function createDefaultDriver()
    {
        return new MyTestDriver();        
    }
    
    // ...
}

configure(new MyTestConfiguration);

参考实际实现以查看其他可能的内容 - 这些类中的几乎所有内容都是 publicprotected,留待您根据需要调用或覆盖。