质量代码在PHP社区中受到高度重视。 你很少会在GitHub上找到未经测试的库。 开拓职员在测试过程中碰着的两个问题是处理文件操作,以及测试内置的PHP函数,如time()orexec()以知足某些期望。 在这篇文章中,阐明了这些案例的一些办理方案。
作为一名PHP开拓职员,您可以轻松进入利用文件或与文件进行交互的情形,特殊是在处理上传的文件或写入缓存或日志文件时。 作为一名精良的开拓职员,您还希望通过测试来涵盖此程序流程。
清单1显示了aCacheWarmer类的简化示例,该示例在warmUp方法中创建缓存目录(如果它尚不存在)。

<?php namespace My\App; class CacheWarmer { private $cacheDirectory; public function __construct(string $cacheDirectory) { $this->cacheDirectory = $cacheDirectory;
}
public function warmUp()
{
if (!is_dir($this->cacheDirectory) &&!mkdir($this->cacheDirectory)) {
throw new \RuntimeException('Could not create cache directory!');
}
// ...
}
}
如果你想在单元测试的帮助下测试这样一个类,最大略的方法便是直接在文件系统中完成。该类在本地进行初始化,调用thewarmUpmethod,系统在末了检讨相应的目录是否已创建并精确添补。虽然这种方法很快且非常有效,但确实涉及各种可能的问题。例如,如果您正在利用除实际目标系统以外的操作系统,则测试职员和开拓职员必须牢记这一点。确保测试之后精确清理文件系统也很主要 - 一方面要避免依赖测试中的副浸染,另一方面要防止自己的文件系统被测试数据淹没。
检讨此类运用程序的更好的办理方案是利用虚拟文件系统。虚拟文件系统是作为PHP的独立流包装器创建的。这可以利用内置的PHP函数,例如mkdirorfile_exist。 vfsStreamlibrary供应这一点。一个优点是vfsStream可以用于险些任何PHP测试框架,例如PHPUnit。 Composer的安装过程与往常一样完成:
composer须要-dev mikey179 / vfsStream
现在,要测试的文件系统在测试完成后才会流式传输和丢弃。这有很大的好处,你不必担心清理和测试的副浸染。更好的I / O操作性能也可以在大型测试套件中发挥浸染。清单2显示了利用PHPUnit和vfsStream的ourCacheWarmer类的可能测试。
<?php namespace My\App\Tests; use My\App\CacheWarmer; use org\bovigo\vfs\vfsStream; use PHPUnit\Framework\TestCase; class CacheWarmerTest extends TestCase { private $root; public function setUp() { $this->root = vfsStream::setup();
}
/
@test
/
public function canCreateCacheDirectoryOnWarmUp()
{
$cacheWarmer = new CacheWarmer($this->root->url() . '/cache');
$cacheWarmer->warmUp();
$this->assertTrue($this->root->hasChild('cache'));
}
}
该库还有许多其他有用的功能,例如权限处理和可调度的磁盘配额。 但是,也该当考虑到这些限定。 例如,与符号链接的交互是不可能的。 更多的文档和例子可以在项目的官方Wiki中找到。
测试内置的PHP函数作为PHP开拓职员常常碰着的另一个问题是利用像time()orexec()这样的内置PHP函数。 这些常日很难测试。 清单3显示了aPdfCreator类的简化版本,它利用wkhtmltopdfinternally创建PDF并通过exec()函数实行它。
<?php
namespace My\App;
class PdfCreator
{
// ...
public function execute(string $htmlFile, string $pdfFile)
{
$output = [];
$returnValue = 0;
exec(sprintf('/usr/bin/wkhtmltopdf %s %s', $htmlFile, $pdfFile), $output, $returnValue);
if ($returnValue !== 0) {
throw new \RuntimeException('Could not create PDF file!');
}
return $output;
}
// ...
}
当然,这个过程可以用适当的软件架构绕过。但是,这并非总是可行的。如果您仍旧想确保运用程序精确处理内置PHP函数的返回值,可以采取几种方法来完成此操作。
单位而不是集成测试第一个常常是显而易见的变体是用功能或集成测试来覆盖这个变体。在这种情形下,您可以在特定条件或高下文下直接实行运用程序,并相应地检讨结果。但是,这些测试常日非常耗时,由于每次都必须准备或初始化测试环境。
为了直接测试内置PHP函数的期望,我们可以利用php-mock。该工具还支持各种测试框架,如PHPUnit或Prophecy(phpspec)。仿照内置PHP函数php-mock利用PHP的命名空间备用规则。它表示,如果利用内置的PHP函数设置为不合格(不带前导反斜杠),则首先在自己的名称空间中搜索利用的内置PHP函数。只有在这之后,才能从全局命名空间获取函数。
PHPUnit的php-mock版本也可以通过Composer安装:
composer须要-dev php-mock / php-mock-phpunit
对付测试,php-mock供应了一个特性,以便我们已经显示的PDFCreatorclass的可能测试看起来如清单4所示。
Listing 4
<?php namespace My\App\Tests; use My\App\PdfCreator; use PHPUnit\Framework\TestCase; class PdfCreatorTest extends TestCase { use \phpmock\phpunit\PHPMock; / @test @expectedException \RuntimeException / public function executeReturnsAnExceptionOnFailure() { $exec = $this->getFunctionMock('My\App', 'exec');
$exec->expects($this->once())->willReturnCallback(
function ($command, &$output, &$returnValue) {
$this->assertEquals('/usr/bin/wkhtmltopdf file.html file.pdf', $command);
$output = ['failure'];
$returnValue = 1;
}
);
$pdfCreator = new PdfCreator;
$pdfCreator->execute('file.html', 'file.pdf');
}
}
这样大略的单元测试显然有很大的上风。 但缺陷是你必须确切知道哪些返回值是可能的。 否则,只管测试覆盖范围广泛,但仍可能发生错 另一个小缺陷是已经提到了对非限定函数调用的限定。 但是这些可以在你自己的代码中轻松设置。
结论和展望乍一看,最明显的测试程序有时是最大的毛病。 利用大量测试套件时尤其如此。 在这种情形下,这里先容的两个库在避免缓慢的集成测试并用单元测试替代它们时可以作为真正的效率助手。 出于这个缘故原由,开拓职员应始终记住这一选项。