2008-08-13 131 views
41

有沒有辦法讓TestCase內部的測試以某種順序運行?例如,我想將對象的生命週期從創建到使用破壞分開,但在運行其他測試之前,我需要確保先設置對象。以某種順序運行PHPUnit測試

+1

如下答案描述,並使用設置()和拆卸()也是一個好主意,你可以添加@depends,但是測試只是從上到下運行... – Andrew 2015-06-10 15:30:28

+0

另外一個似乎沒有被覆蓋的用例:也許所有的測試都是原子性的,但有些測試是慢的。我希望儘快運行快速測試,以便它們可以快速失敗,並且在我已經看到其他問題並且可以立即得到它們之後,任何慢速測試最後都會死掉。 – Kzqai 2016-03-19 21:31:41

回答

44

也許你的測試有一個設計問題。

通常每個測試不能依賴於任何其他測試,因此它們可以按任意順序運行。每個測試都需要實例化並銷燬它需要運行的所有東西,這將是一個完美的方法,你不應該在測試之間共享對象和狀態。

你能更具體地說明爲什麼你需要同一個對象進行N次測試嗎?

+33

這對我來說似乎不正確。單元測試的重點是測試整個單元。擁有一個單位的重點是把事物組合在一起,這些事物必須相互依賴。編寫測試能夠在沒有上下文的情況下測試單個方法的測試類似於提倡對oo進行過程式編程,因爲您主張單個函數不應該依賴於相同的數據。 – doliver 2013-05-04 20:35:42

+3

我不同意你的觀點。實例化測試的輸出是您的測試套件中的其他測試可以使用的有效對象。沒有必要爲每個測試實例化一個新的對象,特別是如果構造器很複雜的話。 – pedromanoel 2013-07-12 15:08:16

+6

如果構造函數很複雜,那麼你做錯了事,可能你的班級做得太多了。請閱讀關於「單一責任模式(SRP)」的更多具體內容「SOLID」,也應該使用mock「測試」測試中的依賴項,請閱讀「嘲笑,假貨和存根」。 – 2013-07-13 22:15:36

1

如果需要以特定順序運行,那麼測試確實存在問題。每個測試應該完全獨立於其他測試:它可以幫助您進行缺陷定位,並允許您獲得可重複(因此可調試)的結果。

結賬this site瞭解如何以避免這些問題的方式來分析測試的整個過程。

8

如果您希望測試共享各種幫助對象和設置,則可以使用setUp()tearDown()添加到sharedFixture屬性。

118

PHPUnit通過@depends註釋支持測試依賴關係。

這裏是從那裏測試將在滿足依賴性的次序來運行該文件的實例,與每個相關的檢驗合格參數傳遞給下一個:

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); 
    } 
} 

然而,重要的是要注意的是測試用未解決的依賴關係將會被執行(可取的,因爲這會迅速引起注意失敗的測試)而不是。所以,在使用依賴關係時要特別注意。

7

PHPUnit允許使用'@depends'註釋來指定相關測試用例,並允許在相關測試用例之間傳遞參數。

2

在我看來,採取以下方案,我需要測試創建和銷燬特定資源。

最初我有兩種方法,a。 testCreateResource和b。 testDestroyResource

a。 testCreateResource

<?php 
$app->createResource('resource'); 
$this->assertTrue($app->hasResource('resource')); 
?> 

b。 testDestroyResource

<?php 
$app->destroyResource('resource'); 
$this->assertFalse($app->hasResource('resource')); 
?> 

我認爲這是一個壞主意,因爲testDestroyResource取決於testCreateResource。更好的做法是做

a。 testCreateResource

<?php 
$app->createResource('resource'); 
$this->assertTrue($app->hasResource('resource')); 
$app->deleteResource('resource'); 
?> 

b。testDestroyResource

<?php 
$app->createResource('resource'); 
$app->destroyResource('resource'); 
$this->assertFalse($app->hasResource('resource')); 
?> 
1

替代解決方案:(!) 使用靜態函數在您的測試來創建可重用元素。舉例來說(我用硒IDE來記錄測試和PHPUnit的硒(github上)內運行的瀏覽器測試)

class LoginTest extends SeleniumClearTestCase 
{ 
    public function testAdminLogin() 
    { 
     self::adminLogin($this); 
    } 

    public function testLogout() 
    { 
     self::adminLogin($this); 
     self::logout($this); 
    } 

    public static function adminLogin($t) 
    { 
     self::login($t, '[email protected]', 'pAs$w0rd'); 
     $t->assertEquals('John Smith', $t->getText('css=span.hidden-xs')); 
    } 

    // @source LoginTest.se 
    public static function login($t, $login, $pass) 
    { 
     $t->open('/'); 
     $t->click("xpath=(//a[contains(text(),'Log In')])[2]"); 
     $t->waitForPageToLoad('30000'); 
     $t->type('name=email', $login); 
     $t->type('name=password', $pass); 
     $t->click("//button[@type='submit']"); 
     $t->waitForPageToLoad('30000'); 
    } 

    // @source LogoutTest.se 
    public static function logout($t) 
    { 
     $t->click('css=span.hidden-xs'); 
     $t->click('link=Logout'); 
     $t->waitForPageToLoad('30000'); 
     $t->assertEquals('PANEL', $t->getText("xpath=(//a[contains(text(),'Panel')])[2]")); 
    } 
} 

好了,現在,我可以用在其他測試這種可重複使用的元素:)例如:

class ChangeBlogTitleTest extends SeleniumClearTestCase 
{ 
    public function testAddBlogTitle() 
    { 
     self::addBlogTitle($this,'I like my boobies'); 
     self::cleanAddBlogTitle(); 
    } 

    public static function addBlogTitle($t,$title) { 
     LoginTest::adminLogin($t); 

     $t->click('link=ChangeTitle'); 
     ... 
     $t->type('name=blog-title', $title); 
     LoginTest::logout($t); 
     LoginTest::login($t, '[email protected]','hilton'); 
     $t->screenshot(); // take some photos :) 
     $t->assertEquals($title, $t->getText('...')); 
    } 

    public static function cleanAddBlogTitle() { 
     $lastTitle = BlogTitlesHistory::orderBy('id')->first(); 
     $lastTitle->delete(); 
    } 
  • 通過這種方式,你可以建立你的測試層次。
  • 您可以保留每個測試用例與其他測試用例完全獨立的屬性(如果您在每次測試後清理數據庫)。
  • 而最重要的,如果舉例來說,在未來的登陸變化的方式,你只能修改LoginTest類,你don'n需要在其他測試正確的登錄部分(他們應該更新LoginTest後工作):)

當我運行測試我的腳本清理數據庫廣告開始。以上我使用我的SeleniumClearTestCase類(我做截圖()和其他很好的功能),它是MigrationToSelenium2(從github,擴展到在Firefox中使用seleniumIDE + ff插件「Selenium IDE:PHP格式化程序」我的類LaravelTestCase(它是Illuminate \ Foundation \ Testing \ TestCase的副本,但不擴展PHPUnit_Framework_TestCase),它是PHPUnit_Extensions_Selenium2TestCase的擴展,當我們希望在測試結束時清理數據庫時,設置laravel以訪問雄辯。爲了設置laravel雄辯,我還在SeleniumClearTestCase函數createApplication(它被稱爲setUp,我從laral test/TestCase中取得這個函數)

2

正確的答案是這是測試的正確配置文件。我有同樣的問題,並通過創建必要的測試文件測試包中固定它下令:

phpunit.xml: 

<phpunit 
     colors="true" 
     bootstrap="./tests/bootstrap.php" 
     convertErrorsToExceptions="true" 
     convertNoticesToExceptions="true" 
     convertWarningsToExceptions="true" 
     strict="true" 
     stopOnError="false" 
     stopOnFailure="false" 
     stopOnIncomplete="false" 
     stopOnSkipped="false" 
     stopOnRisky="false" 
> 
    <testsuites> 
     <testsuite name="Your tests"> 
      <file>file1</file> //this will be run before file2 
      <file>file2</file> //this depends on file1 
     </testsuite> 
    </testsuites> 
</phpunit>