2012-02-26 81 views
4

我創建了自己的自定義PHPUnit約束並將其連接到一個不錯的assert...函數。自定義PHPUnit約束停止工作

我溜它在我的基地TestCase,它的assertLastError功能:

/** 
* abstract, base test-case class. 
*/ 
abstract class TestCase extends \PHPUnit_Framework_TestCase 
{ 
    /** 
    * assert lint of a php file 
    */ 
    protected function assertLint($filename, $message = '') { 
     self::assertThat($filename, new ConstraintLint, $message); 
    } 
    /** 
    * assert the last error 
    */ 
    protected function assertLastError($error, $file, $message = '') { 
     self::assertThat($file, new ConstraintLastError($error), $message); 
    } 
    /** 
    * assert XML DTD validity 
    */ 
    protected function assertXmlStringValidatesDtdUri($xml, $dtd, $message = '') { 
     self::assertThat($dtd, new ConstraintXmlStringValidatesDtdUri($xml), $message); 
    } 

    ... 

我到目前爲止已經調試的約束,我看到evaluate方法被調用和返回FALSE,但是TestRunner的呢不要向我報告失敗。

約束:

/** 
* ConstraintLastError 
* 
* For asserting the last error message in a file. 
* 
* To test trigger_error(). 
* 
* Example: 
* 
* $this->assertLastError('Error Message', 'basenameOfFile.php'); 
* 
*/ 
class ConstraintLastError extends \PHPUnit_Framework_Constraint { 
    private $file; 
    private $error; 
    public function __construct($error) { 
     $this->error = $error; 
    } 
    /** 
    * Evaluates the constraint for parameter $file. Returns TRUE if the 
    * constraint is met, FALSE otherwise. 
    * 
    * @param string $file Value or object to evaluate. 
    * @return bool 
    */ 
    public function evaluate($file) 
    { 
     $this->file = $file; 
     $error = $this->error; 
     $lastError = error_get_last(); 
     if (NULL === $lastError) 
      return false; 

     $last_message = $lastError['message']; 
     $last_file = $lastError['file']; 


     $result = ($error == $last_message && basename($file) == basename($last_file)); 

     var_dump($result, $error, $last_message, $file, $last_file); 

     return $result; 
    } 

    /** 
    * @param mixed $other 
    * @param string $description 
    * @param boolean $not 
    */ 
    protected function customFailureDescription($other, $description, $not) 
    { 
     return sprintf('Failed asserting that the last error %s', basename($other), $not ? '' : 'no ', implode("\n - ", $this->lines)); 
    } 


    /** 
    * Returns a string representation of the constraint. 
    * 
    * @return string 
    */ 
    public function toString() 
    { 
     return sprintf('was %s in file %s.', $this->error, $this->file); 
    } 
} 

我不知道爲什麼這個停止工作,測試用例剛剛結束乾淨,我可以在輸出含看到錯誤消息。 stacktrace(xdebug在打開)並且var_dump告訴我結果爲FALSE。下面是測試:

public function testGetType() 
{ 
    ... 

    $fragment->setParsed(array()); 
    \PHPUnit_Framework_Error_Warning::$enabled = FALSE; 
    $actual = $fragment->getType(); 
    \PHPUnit_Framework_Error_Warning::$enabled = TRUE; 
    $this->assertLastError('Invalid State Error FAIL', 'Fragment.php'); 
} 

這是一個新的測試我剛寫,我在其他地方相同的斷言,以及和他們不工作不再爲好。

PHPUnit的3.6.7

+0

您是否在代碼中設置了自定義錯誤處理程序(set_error_handler)?它的xdebug啓用時,它的工作? – meze 2012-02-26 21:15:51

+0

沒有自定義錯誤處理程序(在我的代碼中)。我不記得xdebug是否在其工作時啓用。我手動停用了xdebug的早期版本的PHPUnit版本,默認情況下我的開發版本xdebug已啓用。所以很有可能xdebug在它的工作時被啓用,但我沒有特別記得。我不知何故我付出了代價,不把我的約束放在考驗之下。 – hakre 2012-02-26 21:23:51

+0

你確定首先觸發錯誤嗎?如果您不禁用PHPUnit的警告 - 異常轉換,會發生什麼情況? – 2012-02-26 22:45:06

回答

3

我現在可以解決這個問題。這與我升級PHPUnit有關,API稍有改動。我已經採取PHPUnit_Framework_Constraint作爲我自己的約束模式了,閱讀裏面的評論有幫助。

evaluate功能現在有所不同,我已將評估邏輯現在移入私人功能並從evaluate切換到matches。它適用於返回值。默認情況下,evaluate不再適用於返回值,但期望拋出異常。爲了完全從中受益,您還可以實例化一些比較對象,但那已經超出了我的頭,可以在asserEquals約束中找到關於該對象的更多信息。

class ConstraintLastError extends \PHPUnit_Framework_Constraint { 

    ... 

    /** 
    * Evaluates the constraint for parameter $file. Returns TRUE if the 
    * constraint is met, FALSE otherwise. 
    * 
    * This method can be overridden to implement the evaluation algorithm. 
    * 
    * @param mixed $other Value or object to evaluate. 
    * @return bool 
    */ 
    public function matches($file) 
    {  
     return $this->compareAgainstLast($file, $this->error); 
    } 

    /** 
    * 
    * @param string $file 
    * @param string $error 
    * @return bool 
    */ 
    private function compareAgainstLast($file, $error) 
    { 
     if (!$last = error_get_last()) 
     { 
      $last = array('message' => '(none)', 'file' => ''); 
     } 

     $this->lastError = $last['message']; 
     $this->lastFile = $last['file']; 

     return $error === $this->lastError && basename($file) === basename($this->lastFile); 
    } 

    /** 
    * @param string $file 
    */ 
    protected function failureDescription($file) 
    { 
     return sprintf('the last error is "%s" in %s, was "%s" in %s' 
        , $this->error, basename($file) 
        , $this->lastError, basename($this->lastFile) 
       ); 
    } 

    ... 

現在就像一個魅力:

1) FragmentTest::testGetType 
Failed asserting that the last error is "Suboptimal State Error" in Fragment.php, was "Invalid State Error" in Fragment.php. 

其他有類似的問題,但不同的解決方案切換到fail它,因爲它在基類Phake Fixes issues #43 and #44實現你可以打電話爲好。

+0

對我來說看起來不錯。 - >失敗也被用於一些phpunits自身的限制方式:https://github.com/sebastianbergmann/phpunit/blob/3.6/PHPUnit/Framework/Constraint/IsIdentical.php - 比較器主要用於獲取PHP ==直接行爲(請參閱:https://github.com/sebastianbergmann/phpunit/blob/3.6/PHPUnit/Framework/Constraint/IsEqual.php),但在您的情況下不需要。感謝分享:) – edorian 2012-02-27 08:36:03

+0

@edorian:感謝您的反饋。你認爲值得向PHPUnit提出這樣的斷言嗎? (我已經修改了一下,所以它更有用,例如文件名可選,更關注實際的錯誤信息)。 – hakre 2012-02-29 01:43:38

+0

我不認爲soa離開PHPUnit_Framework_Error_Warning :: $啓用和'* @expectedException PHPUnit_Framework_Error_Warnings * @expectExceptionMessage myExcpetedErrorMessage'是什麼推薦這些情況下,給用戶另一個選項不值得額外的複雜性,因爲並不是很多人真的需要它。我絕對可以看到你的用例,但我認爲它不適用於很多人。可能是錯誤的,但它總是要爲所有人做出判斷:)也許應該在某個時候進行一次民意調查,並提出建議:) – edorian 2012-02-29 09:13:58