icecave / isolator
全局函数的依赖注入。
Requires
- php: >=5.3
Requires (Dev)
- icecave/archer: ~1
README
Isolator 通过将所有全局函数视为“隔离器”对象上的方法,简化了使用全局函数的类的测试。
composer require icecave/isolator
理由
许多 PHP 扩展(以及 PHP 核心本身)都将其功能实现为全局函数。由于无法用测试替身替换这些函数,因此测试使用这些函数的类很快就会变得困难。
Isolator 通过充当您的类和全局函数之间的代理来尝试解决这个问题。将隔离器实例作为依赖项传递到您的对象中,并在测试时替换任何您希望替换的全局函数调用。
示例
以下类使用 file_get_contents() 来读取文件内容。
class MyDocument { public function __construct($filename) { $this->filename = $filename; } public function getContents() { return file_get_contents($this->filename); } protected $filename; }
尽管示例很简单,但由于该类依赖于文件系统,因此立即变得难以测试。为了测试此类,您可能倾向于在磁盘上设置一些静态的固定值,在设置测试套件时创建一个临时目录,或者甚至使用虚拟文件系统包装器。
Isolator 提供了第四种选择。下面是使用 Isolator
实例重写的相同示例。
use Icecave\Isolator\Isolator; class MyDocument { public function __construct($filename, Isolator $isolator = null) { $this->filename = $filename; $this->isolator = Isolator::get($isolator); } public function getContents() { return $this->isolator->file_get_contents($this->filename); } protected $filename; protected $isolator; }
MyDocument
现在接受一个 Isolator
实例作为其构造函数的一部分。在您的生产代码中每次创建对象时都创建一个新的 Isolator
实例会是一件痛苦且不必要的事情,因此使用 Isolator::get()
方法使共享实例可用。如果将非空值传递给 Isolator::get()
,则返回值不变,允许您在必要时替换隔离器。
MyDocument::getContents()
也已更新为使用隔离器实例而不是直接调用全局函数。MyDocument
的行为保持不变,但测试该类变得容易,如下面的示例测试套件所示。
注意:以下测试是为 PHPUnit 测试框架编写的,使用 Phake 进行模拟。Phake 提供了比 PHPUnit 内置模拟对象更灵活的替代方案。
class MyDocumentTest extends PHPUnit\Framework\TestCase { public function setUp() { // First a mocked isolator instance is created ... $this->isolator = Phake::mock('Icecave\Isolator\Isolator'); // That isolator instance is given to the MyDocument instance // that is to be tested ... $this->myDocument = new MyDocument('foo.txt', $this->isolator); } public function testGetContents() { // Phake is used to configure the mocked isolator to return a known // string when file_get_contents() is called with a parameter equal // to 'foo.txt' ... Phake::when($this->isolator) ->file_get_contents('foo.txt') ->thenReturn('This is the file contents.'); // MyDocument::getContents() is called, and it's result checked ... $contents = $this->myDocument->getContents(); $this->assertEquals($contents, 'This is the file contents.'); // Finally Phake is used to verify that a call to file_get_contents() // was made as expected ... Phake::verify($this->isolator) ->file_get_contents('foo.txt'); } }
该测试验证了 MyDocument
类的行为,而无需任何磁盘访问。
在测试使用保持全局状态或利用数据库、文件系统等外部资源的函数的代码时,使用隔离器最有帮助。通常不需要模拟像 strlen()
这样的确定性函数。
Isolator 特性
在 PHP 5.4 及更高版本中,您还可以使用 IsolatorTrait
将隔离器引入到您的类中。通过 $this->isolator()
访问隔离器实例,并通过 $this->setIsolator()
设置。
use Icecave\Isolator\IsolatorTrait; class MyDocument { use IsolatorTrait; public function __construct($filename) { $this->filename = $filename; } public function getContents() { return $this->isolator()->file_get_contents($this->filename); } protected $filename; }
语言构造
Isolator 还可以用于调用以下类似于函数的语言构造
include
、include_once
、require
和require_once
exit
和die
echo
eval
new
特殊性
PHP的一些核心全局函数在定义方式上存在一些特殊性和不一致性。《隔离器》尝试在可能的情况下适应这种不一致性,但可能与一些参数反射信息非标准或不正确的本地C函数存在问题。这些问题似乎在PHP 5.6版本中得到大量修正。