2008-10-10 51 views
47

我目前正在與PHPUnit一起嘗試開發測試以及我正在編寫的內容,但是,我目前正在編寫會話管理器,並且遇到問題...需要發送頭文件的單元測試

的會話處理類的構造函數是

private function __construct() 
{ 
    if (!headers_sent()) 
    { 
     session_start(); 
     self::$session_id = session_id(); 
    } 
} 

然而,類似PHPUnit發出文本它開始測試前,對這個對象的任何測試返回一個失敗的測試,作爲HTTP「頭「已發送...

+0

我剛剛遇到同樣的問題,但以下答案都沒有工作。你到底做了什麼?請發電子郵件給我。 – 2013-07-17 07:59:57

回答

36

那麼,你的會話管理器基本上被設計破壞了。爲了能夠測試某些東西,必須有可能將其與副作用隔離。不幸的是,PHP是這樣設計的,它鼓勵自由使用全局狀態(echo,header,exit, session_start等)。

您可以做的最好的事情是,隔離組件中的副作用,可以在運行時交換。這樣,您的測試可以使用模擬對象,而實時代碼使用適配器,它具有真正的副作用。你會發現這對單身人士來說並不合適,我猜你會使用它。所以你必須使用其他一些機制來獲取分配給你的代碼的共享對象。你可以從一個靜態註冊表開始,但如果你不介意一點點學習,那麼還有更好的解決方案。

如果你不能這樣做,你總是可以選擇編寫集成測試。例如。使用PHPUnit的相當於WebTestCase

+0

謝謝,我需要這個艱難的愛情!現在我的設計無疑更好,因爲我已經隔離了我班上其他人的環境副作用。 – DaveGauer 2014-03-05 01:10:00

+1

WebTestCase的文檔現在位於http://www.simpletest.org/en/web_tester_documentation.html。 – untill 2015-03-17 12:10:21

0

你不能使用輸出b在開始測試之前苦惱嗎?如果你緩衝了輸出的所有內容,那麼你不應該在設置任何頭文件時遇到問題,因爲在這一點上沒有輸出到客戶端。

即使OB在類內部的某處使用,它也是可堆疊的,並且OB不應該影響裏面的內容。

+0

似乎ob_start()實際上並不重置PHP是否認爲頭文件已發送。 – Mez 2008-10-10 06:19:33

+0

是的,ob_start不能使用標題,所以即使你使用它,它仍然會抱怨如果標題不能再發送。我可以編輯這個PHPUnit雖然... – Mez 2008-10-10 06:25:03

0

就我所知,Zend Framework對Zend_Session包測試使用相同的輸出緩衝。你可以看看他們的測試案例,讓你開始。

5

我認爲「正確的」解決方案是創建一個非常簡單的類(它非常簡單,不需要測試),它是PHP會話相關函數的包裝,並使用它來代替調用session_start()等。直。

在測試中傳遞模擬對象而不是真正的有狀態,不可測試的會話類。

private function __construct(SessionWrapper $wrapper) 
{ 
    if (!$wrapper->headers_sent()) 
    { 
     $wrapper->session_start(); 
     $this->session_id = $wrapper->session_id(); 
    } 
} 
20

創建PHPUnit的引導文件,其中要求:

session_start(); 

這樣便開始PHPUnit的:

phpunit --bootstrap pathToBootstrap.php --anotherSwitch /your/test/path/ 

的引導文件獲取一切之前調用,所以頭不是招沒有被髮送,一切都應該正常工作。

+1

如果您使用的是XML配置文件,您可以通過向您的根'`元素添加引導屬性來設置引導:` ...` – 2012-02-10 17:25:48

0

創建bootstrap文件,指出4回帖子似乎是最清潔的方式。

通常在PHP中,我們不得不維護並嘗試爲傳統項目添加某種工程學科,這些項目是非常糟糕的。我們沒有時間(或權威)把整堆垃圾拋開並重新開始,所以第一次使用troelskn並不總是可行的。 (如果我們可以回到最初的設計,那麼我們可以完全拋棄PHP並使用更現代的東西,比如ruby或者python,而不是幫助web開發世界的這個COBOL永久化。)

如果你是試圖爲使用session_start或setcookie的模塊編寫單元測試,而不是在boostrap文件中啓動會話,從而避免這些問題。

19

phpUnit在測試運行時打印輸出,因此即使在第一次測試中也會導致headers_sent()返回true。

要解決整個測試套件的這個問題,您只需在設置腳本中使用ob_start()。

例如,假設您有一個名爲AllTests.php的文件,它是phpUnit加載的第一個文件。該腳本可能類似於以下內容:

<?php 

ob_start(); 

require_once 'YourFramework/AllTests.php'; 

class AllTests { 
    public static function suite() { 
     $suite = new PHPUnit_Framework_TestSuite('YourFramework'); 
     $suite->addTest(YourFramework_AllTests::suite()); 
     return $suite; 
    } 
} 
0

,因爲我單元測試我的引導,現在(是的,我知道你最不這樣做),我同樣的問題在運行(包括頭()和session_start())。 我找到了解決辦法很簡單,在你的單元測試自舉定義一個常數,簡單地發送標題或啓動會話前檢查:

// phpunit_bootstrap.php 
define('UNITTEST_RUNNING', true); 

// bootstrap.php (application bootstrap) 
defined('UNITTEST_RUNNING') || define('UNITTEST_RUNNING', false); 
..... 
if(UNITTEST_RUNNING===false){ 
    session_start(); 
} 

我同意這是不完美的設計,但我單元測試現有的應用程序,不需要重寫大部件。我也使用相同的邏輯來使用__call()和__set()魔術方法來測試私有方法。

public function __set($name, $value){ 
    if(UNITTEST_RUNNING===true){ 
     $name='_' . $name; 
     $this->$name=$value; 
    } 
    throw new Exception('__set() can only be used when unittesting!'); 
} 
11

我有同樣的問題,我通過調用PHPUnit的與--stderr標誌就這樣解決了這個問題:

phpunit --stderr /path/to/your/test 

希望它可以幫助別人!

1

我不知道爲什麼沒有人列出了XDebug選項:

/** 
* @runInSeparateProcess 
* @requires extension xdebug 
*/ 
public function testGivenHeaderIsIncludedIntoResponse() 
{ 
    $customHeaderName = 'foo'; 
    $customHeaderValue = 'bar'; 

    // Here execute the code which is supposed to set headers 
    // ... 

    $expectedHeader = $customHeaderName . ': ' . $customHeaderValue; 
    $headers = xdebug_get_headers(); 

    $this->assertContains($expectedHeader, $headers); 
} 
0

看來你需要注入的會話,以便您可以測試你的代碼。我使用的最佳選項是Aura.Auth,用於身份驗證過程並使用NullSession和NullSegment進行測試。

Aura testing with null sessions

靈氣框架寫得很美,你可以對自己使用Aura.Auth沒有任何其他的靈氣框架依賴。

相關問題