2014-07-17 33 views
0

我有以下情況。比方說,我有兩個類:單元測試交換父類

class Session { 
    public function start() { 
     return session_start(); 
    } 

    // methods for all the other session functions of PHP 
} 

並延伸Session

class TrustedSession extends Session { 
    public function start() { 
     if(parent::start() === false) 
      return false; 

     $requestRemoteAddress = isset($_SERVER['REMOTE_ADDR'])?$_SERVER['REMOTE_ADDR']:null; 
     $requestUserAgent = isset($_SERVER['HTTP_USER_AGENT'])?$_SERVER['HTTP_USER_AGENT']:null; 

     $trustedRemoteAddress = $this->get('TRUSTED_REMOTE_ADDR'); 
     $prevUserAgent = $this->get('PREV_USERAGENT'); 

     if($trustedRemoteAddress !== $requestRemoteAddress || $prevUserAgent !== $requestUserAgent) 
      $this->regenerateID(true); 

     return true; 
    } 
} 

所以沒有我想測試我的TrustedSession類的子類。但我必須使用Session的模擬,因爲according to this question here我不能使用PHP的真實會話功能,因爲它們導致PHPUnit出現Output already started錯誤。

現在的問題:如何模擬Session類。我的意思是我可以創建一個SessionInterface,它定義了我必須實現的方法Session及其模型。但是,我怎樣才能用模型擴展我的TrustedSession類呢?

我的想法是在運行時交換父類,但我認爲這是不可能的(至少在PHP中)。這是一個設計問題,我正在考慮上面介紹的接口,但這並不能幫助我擺脫這種情況。有沒有一個「乾淨」,很好的解決方案?

+0

爲什麼你不直接模擬TrustedSession ?.你可以模擬從父類繼承的方法。 – gontrollez

+0

@gontrollez如果測試'TrustedSession',如果它是模擬的,我該如何測試?可能我無法按照你的想法。如果你能在答案中告訴我你在想什麼,這將是很好的.-) – TiMESPLiNTER

回答

-1

我回答這個問題我自己,因爲我有一個方法來解決你可以注入會話模擬這個問題與Output already started在使用PHPUnit進行測試時結合向客戶端發送頭文件的PHP函數(如session_start()一樣)。

這真的很簡單,但也非常hacky,肯定不潔。我所做的是我使用phpunit.xml的選項來執行引導文件。

<phpunit bootstrap="bootstrap.php" colors="true"> 
    <!-- testsuites here --> 
</phpunit> 

而在bootstrap.php文件我把這些代碼行:

ob_start(); 

// Composer autoload file 
require '../vendor/autoload.php'; 

現在所有的PHPUnit發送到客戶端的輸出獲取緩存,直到腳本結束。這適用於3.7.*版本的PHPUnit。由於我正在使用PHPStorm進行開發,因此尚未使用版本4.x進行測試。

這也只適用於標準的單元測試,它代碼覆蓋失敗,因爲在我的bootstrap.php文件中開始的代碼覆蓋模塊中的某處強制「清理併發送到客戶端」輸出緩衝。

+0

這個答案與你的問題無關。沒有人可以知道你需要什麼類的會話類。如果你問過「輸出已經開始的問題」,也許你可以得到更好的解決方案。 – gontrollez

+0

那麼問題顯然是關於'輸出已經開始問題'的問題(我也與這個問題有關的問題),我尋找一個乾淨的解決方案。正如我所提到的,我對我的回答並不是100%滿意,但這是我能給我的問題最好的。我想過把這些類換成乾淨的解決方案,但這是不可能的,所以我最終用'ob_start()'作爲解決方案。但是,問題標題有點令人費解。 – TiMESPLiNTER

0

定義一個接口,使會議TrustedSession的依賴性:

interface ISession 
{ 
    public function start(); 
} 

class Session implements ISession 
{ 
    public function start() 
    { 
     return session_start(); 
    } 
} 

class TrustedSession implements ISession 
{ 
    private $session; 

    public function __construct(Session $session) 
    { 
     $this->session = $session; 
    } 

    public function start() 
    { 
     if (!$this->session->start()) { 
      return false; 
     } 

     $requestRemoteAddress = isset($_SERVER['REMOTE_ADDR'])?$_SERVER['REMOTE_ADDR']:null; 
     .... 

     return true; 

    } 
} 

這種方式測試TrustedSession時

+0

是的,你的第二個想法也是我的想法。這將是一個很好的解決方案,但問題在於,這是沒有意義的,因爲'TrustedSession'必須與'Session'具有完全相同的方法......並且它實際上是對基本會話類的擴展,因爲它提高了'Session'的功能... – TiMESPLiNTER

+0

然後,您可以定義Session和TrustedSession應實現的接口,並使Session成爲TrustedSession的依賴項。 – gontrollez

+0

我想不出一個很好的實施方案。你可能會在你的答案中嘲笑它嗎? – TiMESPLiNTER