# 全局狀態
[使用單件(singleton)的代碼很難測試。](http://googletesting.blogspot.com/2008/05/tott-using-dependancy-injection-to.html)使用全局變量的代碼也一樣。通常情況下,欲測代碼和全局變量之間會強烈耦合,并且其創建無法控制。另外一個問題是,一個測試對全局變量的改變可能會破壞另外一個測試。
在 PHP 中,全局變量是這樣運作的:
- 全局變量 `$foo = 'bar';` 實際上是存儲為 `$GLOBALS['foo'] = 'bar';` 的。
- `$GLOBALS`這個變量是一種被稱為*超全局*變量的變量。
- 超全局變量是一種在任何變量作用域中都總是可用的內建變量。
- 在函數或者方法的變量作用域中,要訪問全局變量 `$foo`,可以直接訪問 `$GLOBALS['foo']`,或者用 `global $foo;` 來創建一個引用全局變量的局部變量。
除了全局變量,類的靜態屬性也是一種全局狀態。
默認情況下,PHPUnit 用一種更改全局變量與超全局變量(`$GLOBALS`、`$_ENV`、`$_POST`、`$_GET`、`$_COOKIE`、`$_SERVER`、`$_FILES`、`$_REQUEST`)不會影響到其他測試的方式來運行所有測試。同時,還可以選擇將這種隔離擴展到類的靜態屬性。
>[info] ### Note
> 對全局變量和類的靜態屬性的備份與還原操作使用了 `serialize()` 與 `unserialize()`。
某些類的實例對象(比如 `PDO`)無法序列化,因此如果把這樣一個對象存放在比如說 `$GLOBALS` 數組內時,備份操作就會出問題。
在[the section called “@backupGlobals”](# "@backupGlobals")中所討論的 `@backupGlobals` 標注可以用來控制對全局變量的備份與還原操作。另外,還可以提供一個全局變量的黑名單,黑名單中的全局變量將被排除于備份與還原操作之外,就像這樣:
~~~
class MyTest extends PHPUnit_Framework_TestCase
{
protected $backupGlobalsBlacklist = array('globalVariable');
// ...
}
~~~
>[info] ### Note
> 在方法(例如 `setUp()` 方法)內對 `$backupGlobalsBlacklist` 屬性進行設置是無效的。
在 [the section called “@backupStaticAttributes”](# "@backupStaticAttributes") 中提到的 `@backupStaticAttributes` 標注可以用于在每個測試之前備份所有已聲明類的靜態屬性值并在其后恢復。
它所處理的并不只是測試類自身,而是在測試開始時已聲明的所有類。它只作用于靜態類屬性,不作用于函數內聲明的靜態變量。
>[info] ### Note
> 只有啟用了 `@backupStaticAttributes` 的測試方法才會在方法之前執行此操作。如果在此之前運行的某個沒有啟用 `@backupStaticAttributes` 的測試方法改變了靜態屬性的值,那么被備份及還原的將會是這個改變后的值——而非初始聲明時提供的默認值。PHP 并不額外記錄任何靜態變量的聲明時提供的初始默認值。
同樣的情況也發生于測試內部新加載/聲明的類的靜態屬性上。它們也無法在測試結束之后復原為聲明時提供的原始默認值,因為無從得知這些默認值。這些被修改過的值會泄漏到后繼測試中。
對單元測試而言,推薦在 `setUp()` 中顯式的重置測試中使用到的靜態屬性(最好同時在 `tearDown()` 中執行重置,這樣就保證不會影響到后繼的測試)。
可以提供黑名單來將靜態屬性從備份與還原操作中排除出去:
~~~
class MyTest extends PHPUnit_Framework_TestCase
{
protected $backupStaticAttributesBlacklist = array(
'className' => array('attributeName')
);
// ...
}
~~~
>[info] ### Note
> 在方法(例如 `setUp()` )內對 `$backupStaticAttributesBlacklist` 屬性進行設置是無效的。
- 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. 版權