# 對文件系統進行模仿
[vfsStream](https://github.com/mikey179/vfsStream) 是對[虛擬文件系統](http://en.wikipedia.org/wiki/Virtual_file_system) 的 [流包覆器(stream wrapper)](http://www.php.net/streams),可以用于模仿真實文件系統,在單元測試中可能會有所助益。
如果使用 [Composer](https://getcomposer.org/) 來管理項目的依賴關系,那么只需簡單的在項目的 `composer.json` 文件中加一條對 `mikey179/vfsStream` 的依賴關系即可。以下是一個最小化的 `composer.json`文件例子,只定義了一條對 PHPUnit 4.6 與 vfsStream 的開發時(development-time)依賴:
~~~
{
"require-dev": {
"phpunit/phpunit": "~4.6",
"mikey179/vfsStream": "~1"
}
}
~~~
[Example?9.21, “一個與文件系統交互的類”](# "Example?9.21.?一個與文件系統交互的類")展示了一個與文件系統交互的類。
**Example?9.21.?一個與文件系統交互的類**
~~~
<?php
class Example
{
protected $id;
protected $directory;
public function __construct($id)
{
$this->id = $id;
}
public function setDirectory($directory)
{
$this->directory = $directory . DIRECTORY_SEPARATOR . $this->id;
if (!file_exists($this->directory)) {
mkdir($this->directory, 0700, TRUE);
}
}
}?>
~~~
如果不使用諸如 vfsStream 這樣的虛擬文件系統,就無法在隔離外部影響的情況下對 `setDirectory()` 方法進行測試(參見 [Example?9.22, “對一個與文件系統交互的類進行測試”](# "Example?9.22.?對一個與文件系統交互的類進行測試"))。
**Example?9.22.?對一個與文件系統交互的類進行測試**
~~~
<?php
require_once 'Example.php';
class ExampleTest extends PHPUnit_Framework_TestCase
{
protected function setUp()
{
if (file_exists(dirname(__FILE__) . '/id')) {
rmdir(dirname(__FILE__) . '/id');
}
}
public function testDirectoryIsCreated()
{
$example = new Example('id');
$this->assertFalse(file_exists(dirname(__FILE__) . '/id'));
$example->setDirectory(dirname(__FILE__));
$this->assertTrue(file_exists(dirname(__FILE__) . '/id'));
}
protected function tearDown()
{
if (file_exists(dirname(__FILE__) . '/id')) {
rmdir(dirname(__FILE__) . '/id');
}
}
}
?>
~~~
上面的方法有幾個缺點:
-
和任何其他外部資源一樣,文件系統可能會間歇性的出現一些問題,這使得和它交互的測試變得不可靠。
-
在 `setUp()` 和 `tearDown()` 方法中,必須確保這個目錄在測試前和測試后均不存在。
-
如果測試在 `tearDown()` 方法被調用之前就終止了,這個目錄就會遺留在文件系統中。
[Example?9.23, “在對與文件系統交互的類進行的測試中模仿文件系統”](# "Example?9.23.?在對與文件系統交互的類進行的測試中模仿文件系統")展示了如何在對與文件系統交互的類進行的測試中使用 vfsStream 來模仿文件系統。
**Example?9.23.?在對與文件系統交互的類進行的測試中模仿文件系統**
~~~
<?php
require_once 'vfsStream/vfsStream.php';
require_once 'Example.php';
class ExampleTest extends PHPUnit_Framework_TestCase
{
public function setUp()
{
vfsStreamWrapper::register();
vfsStreamWrapper::setRoot(new vfsStreamDirectory('exampleDir'));
}
public function testDirectoryIsCreated()
{
$example = new Example('id');
$this->assertFalse(vfsStreamWrapper::getRoot()->hasChild('id'));
$example->setDirectory(vfsStream::url('exampleDir'));
$this->assertTrue(vfsStreamWrapper::getRoot()->hasChild('id'));
}
}
?>
~~~
這有幾個優點:
-
測試本身更加簡潔。
-
vfsStream 讓開發者能夠完全控制被測代碼所處的文件系統環境。
-
由于文件系統操作不再對真實文件系統進行操作,`tearDown()` 方法中的清理操作不再需要了。
- PHPUnit 手冊
- 1. 安裝 PHPUnit
- 需求
- PHP 檔案包 (PHAR)
- Composer
- 可選的組件包
- 2. 編寫 PHPUnit 測試
- 測試的依賴關系
- 數據供給器
- 對異常進行測試
- 對 PHP 錯誤進行測試
- 對輸出進行測試
- 錯誤相關信息的輸出
- 3. 命令行測試執行器
- 命令行選項
- 4. 基境(fixture)
- setUp() 多 tearDown() 少
- 變體
- 基境共享
- 全局狀態
- 5. 組織測試
- 用文件系統來編排測試套件
- 用 XML 配置來編排測試套件
- 6. 有風險的測試
- 無用測試
- 意外的代碼覆蓋
- 測試執行期間產生的輸出
- 測試執行時長的超時限制
- 全局狀態篡改
- 7. 未完成的測試與跳過的測試
- 未完成的測試
- 跳過測試
- 用 @requires 來跳過測試
- 8. 數據庫測試
- 數據庫測試所支持的供應商
- 數據庫測試的難點
- 數據庫測試的四個階段
- PHPUnit 數據庫測試用例的配置
- 理解 DataSet(數據集)和 DataTable(數據表)
- 數據庫連接 API
- 數據庫斷言 API
- 常見問題(FAQ)
- 9. 測試替身
- Stubs (樁件)
- 仿件對象(Mock Object)
- Prophecy
- 對特質(Trait)與抽象類進行模仿
- 對 Web 服務(Web Services)進行上樁或模仿
- 對文件系統進行模仿
- 10. 測試實踐
- 在開發過程中
- 在調試過程中
- 11. 代碼覆蓋率分析
- 用于代碼覆蓋率的軟件衡量標準
- 包含與排除文件
- 略過代碼塊
- 指明要覆蓋的方法
- 邊緣情況
- 12. 測試的其他用途
- 敏捷文檔
- 跨團隊測試
- 13. Logging (日志記錄)
- 測試結果 (XML)
- 測試結果 (TAP)
- 測試結果 (JSON)
- 代碼覆蓋率 (XML)
- 代碼覆蓋率 (TEXT)
- 14. 擴展 PHPUnit
- 從 PHPUnit_Framework_TestCase 派生子類
- 編寫自定義斷言
- 實現 PHPUnit_Framework_TestListener
- 從 PHPUnit_Extensions_TestDecorator 派生子類
- 實現 PHPUnit_Framework_Test
- A. 斷言
- assertArrayHasKey()
- assertClassHasAttribute()
- assertArraySubset()
- assertClassHasStaticAttribute()
- assertContains()
- assertContainsOnly()
- assertContainsOnlyInstancesOf()
- assertCount()
- assertEmpty()
- assertEqualXMLStructure()
- assertEquals()
- assertFalse()
- assertFileEquals()
- assertFileExists()
- assertGreaterThan()
- assertGreaterThanOrEqual()
- assertInfinite()
- assertInstanceOf()
- assertInternalType()
- assertJsonFileEqualsJsonFile()
- assertJsonStringEqualsJsonFile()
- assertJsonStringEqualsJsonString()
- assertLessThan()
- assertLessThanOrEqual()
- assertNan()
- assertNull()
- assertObjectHasAttribute()
- assertRegExp()
- assertStringMatchesFormat()
- assertStringMatchesFormatFile()
- assertSame()
- assertStringEndsWith()
- assertStringEqualsFile()
- assertStringStartsWith()
- assertThat()
- assertTrue()
- assertXmlFileEqualsXmlFile()
- assertXmlStringEqualsXmlFile()
- assertXmlStringEqualsXmlString()
- B. 標注
- @author
- @after
- @afterClass
- @backupGlobals
- @backupStaticAttributes
- @before
- @beforeClass
- @codeCoverageIgnore*
- @covers
- @coversDefaultClass
- @coversNothing
- @dataProvider
- @depends
- @expectedException
- @expectedExceptionCode
- @expectedExceptionMessage
- @expectedExceptionMessageRegExp
- @group
- @large
- @medium
- @preserveGlobalState
- @requires
- @runTestsInSeparateProcesses
- @runInSeparateProcess
- @small
- @test
- @testdox
- @ticket
- @uses
- C. XML 配置文件
- PHPUnit
- 測試套件
- 分組
- 為代碼覆蓋率包含或排除文件
- Logging (日志記錄)
- 測試監聽器
- 設定 PHP INI 設置、常量、全局變量
- 為 Selenium RC 配置瀏覽器
- D. 升級
- E. 索引
- F. 參考書目
- G. 版權