# 測試的依賴關系
> 單元測試主要是作為一種良好實踐來編寫的,它能幫助開發人員識別并修復 bug、重構代碼,還可以看作被測軟件單元的文檔。要實現這些好處,理想的單元測試應當覆蓋程序中所有可能的路徑。一個單元測試通常覆蓋一個函數或方法中的一個特定路徑。但是,測試方法并不一定非要是一個封裝良好的獨立實體。測試方法之間經常有隱含的依賴關系暗藏在測試的實現方案中。
> --Adrian Kuhn et. al.
PHPUnit支持對測試方法之間的顯式依賴關系進行聲明。這種依賴關系并不是定義在測試方法的執行順序中,而是允許生產者(producer)返回一個測試基境(fixture)的實例,并將此實例傳遞給依賴于它的消費者(consumer)們。
- 生產者(producer),是能生成被測單元并將其作為返回值的測試方法。
- 消費者(consumer),是依賴于一個或多個生產者及其返回值的測試方法。
[Example?2.2, “用 `@depends` 標注來表達依賴關系”](# "Example?2.2.?用 @depends 標注來表達依賴關系")展示了如何用 `@depends` 標注來表達測試方法之間的依賴關系。
**Example?2.2.?用 `@depends` 標注來表達依賴關系**
~~~
<?php
class StackTest extends PHPUnit_Framework_TestCase
{
public function testEmpty()
{
$stack = array();
$this->assertEmpty($stack);
return $stack;
}
/**
* @depends testEmpty
*/
public function testPush(array $stack)
{
array_push($stack, 'foo');
$this->assertEquals('foo', $stack[count($stack)-1]);
$this->assertNotEmpty($stack);
return $stack;
}
/**
* @depends testPush
*/
public function testPop(array $stack)
{
$this->assertEquals('foo', array_pop($stack));
$this->assertEmpty($stack);
}
}
?>
~~~
在上例中,第一個測試, `testEmpty()`,創建了一個新數組,并斷言其為空。隨后,此測試將此基境作為結果返回。第二個測試,`testPush()`,依賴于 `testEmpty()` ,并將所依賴的測試之結果作為參數傳入。最后,`testPop()` 依賴于 `testPush()`。
>[info] ### Note
> 默認情況下,生產者所產生的返回值將“原樣”傳遞給相應的消費者。這意味著,如果生產者返回的是一個對象,那么傳遞給消費者的將是一個指向此對象的引用。如果需要傳遞對象的副本而非引用,則應當用 `@depends clone` 替代 `@depends`。
為了快速定位缺陷,我們希望把注意力集中于相關的失敗測試上。這就是為什么當某個測試所依賴的測試失敗時,PHPUnit 會跳過這個測試。通過利用測試之間的依賴關系,缺陷定位得到了改進,如[Example?2.3, “利用測試之間的依賴關系”](# "Example?2.3.?利用測試之間的依賴關系")中所示。
**Example?2.3.?利用測試之間的依賴關系**
~~~
<?php
class DependencyFailureTest extends PHPUnit_Framework_TestCase
{
public function testOne()
{
$this->assertTrue(FALSE);
}
/**
* @depends testOne
*/
public function testTwo()
{
}
}
?>
~~~
~~~
phpunit --verbose DependencyFailureTest
PHPUnit 5.0.0 by Sebastian Bergmann and contributors.
FS
Time: 0 seconds, Memory: 5.00Mb
There was 1 failure:
1) DependencyFailureTest::testOne
Failed asserting that false is true.
/home/sb/DependencyFailureTest.php:6
There was 1 skipped test:
1) DependencyFailureTest::testTwo
This test depends on "DependencyFailureTest::testOne" to pass.
FAILURES!
Tests: 1, Assertions: 1, Failures: 1, Skipped: 1.
~~~
測試可以使用多個 `@depends` 標注。PHPUnit 不會更改測試的運行順序,因此你需要自行保證某個測試所依賴的所有測試均出現于這個測試之前。
擁有多個 `@depends` 標注的測試,其第一個參數是第一個生產者提供的基境,第二個參數是第二個生產者提供的基境,以此類推。參見[Example?2.4, “有多重依賴的測試”](# "Example?2.4.?有多重依賴的測試")
**Example?2.4.?有多重依賴的測試**
~~~
<?php
class MultipleDependenciesTest extends PHPUnit_Framework_TestCase
{
public function testProducerFirst()
{
$this->assertTrue(true);
return 'first';
}
public function testProducerSecond()
{
$this->assertTrue(true);
return 'second';
}
/**
* @depends testProducerFirst
* @depends testProducerSecond
*/
public function testConsumer()
{
$this->assertEquals(
array('first', 'second'),
func_get_args()
);
}
}
?>
~~~
~~~
phpunit --verbose MultipleDependenciesTest
PHPUnit 5.0.0 by Sebastian Bergmann and contributors.
...
Time: 0 seconds, Memory: 3.25Mb
OK (3 tests, 3 assertions)
~~~
- 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. 版權