namelesscoder / numerolog-phpunit
基于Numerolog的PHPUnit统计断言
Requires
- namelesscoder/numerolog: ^1.2
- phpunit/phpunit: ^4.8
Requires (Dev)
README
提供一个新类型的测试用例基类,该类包含基于Numerolog收集和分析的统计信息的额外断言方法。
Numerolog使用远程服务器(默认情况下使用一个免费的公共服务器)来存储值 - 此包将其与PHPUnit集成以创建断言,例如,如果值增加则会导致失败;并且随着时间的推移,当值下降时,预期最大值会越来越低。
预期用途
- 基于统计信息而不是硬编码值的断言。
- 一种自动“学习”的方式来衡量迭代开发期间代码的性能,其中每个断言都会为下一个断言“提高标准”。
- 以分布式方式执行,适合与Travis等CI平台兼容。
断言**可以用于跟踪执行时间**,但应非常小心地用于此目的:一致性是**至关重要的**,这意味着在断言基于时间的统计时,分布式测试可能不是明智的选择。
断言自然适合跟踪如下值
- 昂贵函数的内存使用。
- 调用栈分析;最大调用深度,调用函数的数量等。
- 循环复杂度跟踪;防止峰值增加/减少。
- 代码行数与注释行数的比率;防止减少。
- 文件大小。
- 等等。
在具有Numerolog令牌的系统上成功执行断言将导致记录新值。换句话说:认证用户和系统都运行测试并记录统计数据。
然后可以使用标准的Numerolog命令和集成(以及其他用于生成图表的事物)检索由numerolog-phpunit生成的统计数据。
背景知识
- 不用说,但还是要说:**此包与远程主机通信**(您可以选择覆盖)。传输的数据与您使其匿名程度相同:使用一个令牌,这是半公共信息,您可以在多个主机上共享(但应该加密)。并且您的包名称将被包括在内,以及您访问的**计数器名称**。因此,不要在计数器名称或包名称中使用任何秘密信息,并保护您的令牌。
- 您的项目根目录必须包含一个
composer.json
,并且此文件必须包含一个name
条目,该条目必须由vendor/package-name
格式组成。供应商和包用于在Numerolog中识别您的包。 - 您需要了解关于Numerolog令牌的知识。虽然公共测试不需要令牌,但只有拥有令牌的项目才能进行测试和**更新**统计数据。在此处了解关于Numerlog令牌的更多信息。
- 默认情况下,您的测试将使用**公共**Numerolog服务器,但您可以将它更改为您自己设置的服务器。要了解更多信息,请了解Numerolog的配置。
- 当提供令牌时,Numerolog可能会根据您的令牌实施速率限制,当未提供令牌时,则根据您的IP地址实施限制。由于每个请求都非常轻量级,并且每个测试运行都会期望许多请求,因此提供了一个非常慷慨的限制。如果您的项目生成的请求数量超过允许的数量,唯一增加或移除此限制的方法是使用您自己的Numerolog端点。
如果您从公共服务器开始,后来需要迁移,作者将很高兴为您提供数据存储文件以供传输——前提是它们包含大量数据!如果您的存储非常轻量,请在您的端点自行重新创建它们。
用法
通过composer使用 composer require namelesscoder/numerolog-phpunit
进行安装,然后使用 \NamelessCoder\NumerologPhpunit\StatisticsTestCase
作为测试用例的父类(其他测试用例遵循所有phpunit规则)。或者,您也可以使用 \NamelessCoder\NumerologPhpunit\StatisticsTestCaseTrait
作为类中的特性;对于更适合您单元测试结构的情况。这两种方法将为您的测试用例提供相同的功能。
有五种类型的断言可以与各种统计数据进行比较
$this->assertLessThan******($counterName, $value, $count = 20); $this->assertLessThanOrEqualTo******($counterName, $value, $count = 20); $this->assertEqualTo******($counterName, $value, $count = 20); $this->assertGreaterThan******($counterName, $value, $count = 20); $this->assertGreaterThanOrEqualTo******($counterName, $value, $count = 20);
其中 $counterName
是单个计数器的 lowerCamelCase
名称;$value
是您要比较的新值;$count
是从历史中提取并用于比较数据集的值的数量。
其中 ******
可以是以下四个统计参数之一
平均值
最小值
最大值
总和
这意味着总共有20(5 x 4)种简单的统计断言方法。
您的测试方法还可以执行以下更高级的断言
// Success only if $value has a standard deviation inside specified tolerance: $this->assertWithinStandardDeviation($counterName, $value, $allowedStandardDeviation = 1, $count = 20); // Success if $value is above current minimum and below current maximum: $this->assertWithinSetRange($counterName, $value, $count = 20); // Opposite of the above $this->assertNotWithinSetRange($counterName, $value, $count = 20); // Success only if $value exists as an exact match (also for floats!) in set: $this->assertExactlyWithinSetRange($counterName, $value, $count = 20); // Opposite of the above $this->assertNotExactlyWithinSetRange($counterName, $value, $count = 20);
示例
当组合在一起时,一个完整的统计单元测试函数可以看起来像
public function testExpectedMemoryUsageOfMyFunctionOnMyClassIsSameOrLower() { $subject = new MyClass('myconstructorvalue'); $monitor = new Monitor($subject); $subject->doSomethingMemoryIntensive(1000); $usage = $monitor->getMemoryUsage(); // method always uses memory; no usage or freed memory is an early failure: $this->assertGreaterThan(0, $usage); // assertion: no more than 2 standard deviations allowed. Include 40 values in set: $this->assertWithinStandardDeviation('myFunctionMemoryUsage', $usage, 2, 40); // assertion: value should be less than or equal to average recorded usage: $this->assertLessThanOrEqualToAverage('myFunctionMemoryUsage', $usage, 40); }
Monitor
类不包括在内,是假设的。可以使用任何测量方法。在这个测试中,我们让 $subject
做一些已知会大量使用内存的操作 - 然后,断言我们目前所在的代码库中的使用情况没有超过记录的平均值两个标准差。我们还断言使用情况要么小于或等于记录的平均值。
假设在执行断言的项目中存在Numerolog令牌,每次成功的断言都会添加到统计历史记录中。在这种情况下,我们不断地测试我们的内存使用量不会 增加;同时也测试它不会突然 急剧下降。这意味着,随着您改进要测试的代码,Numerolog确保您的测试也“学习”了可以期望的内容,而无需您像通常使用单元测试那样不断修改测试用例以改变期望。
陷阱
如果您正在测试的代码依赖于框架或具有其他依赖项,请确保您在PHPUnit中充分模拟所有这些依赖项,否则,由于框架或依赖项中发生的变化,数字可能被意外地扭曲。例如,在假设的情况下,如果您有一个作为依赖项的Symfony组件,而这个组件的性能突然下降(无论原因是什么),那么如果您没有充分模拟该依赖项,测试可能会失败。显然,PHPUnit代码本身也计算在内,除非您小心,例如,不要在您分析代码窗口中包含对断言方法或模拟生成的调用。
基本上:使用 适当的 单元测试设计,避免在您配置文件和跟踪的变量中出现意外问题。