2016-08-31 97 views
2

我環顧四周,發現了一個適用於普通對象的解決方案,但它似乎不適用於嘲笑。如何爲抽象類設置mock的受保護屬性?

以下測試失敗,消息:Unable to set property someProperty of object type Mock_ClassToTest_40ea0b83: Property someProperty does not exist

class sampleTestClass extends PHPUnit_Framework_TestCase 
{ 
    function test() { 
     $object = $this->getMockForAbstractClass(ClassToTest::class, [], '', false); 
     $this->setProtectedProperty($object, 'someProperty', 'value'); 
    } 

    private function getReflectionProperty($object, $property) { 
     $reflection   = new ReflectionClass($object); 
     $reflectionProperty = $reflection->getProperty($property); 
     $reflectionProperty->setAccessible(true); 
     return $reflectionProperty; 
    } 

    /** 
    * This method modifies the protected properties of any object. 
    * @param object $object The object to modify. 
    * @param string $property The name of the property to modify. 
    * @param mixed $value The value to set. 
    * @throws TestingException 
    */ 
    function setProtectedProperty(&$object, $property, $value) { 
     try { 
      $reflectionProperty = $this->getReflectionProperty($object, $property); 
      $reflectionProperty->setValue($object, $value); 
     } 
     catch (Exception $e) { 
      throw new TestingException("Unable to set property {$property} of object type " . get_class($object) . 
             ': ' . $e->getMessage(), 0, $e); 
     } 
    } 
} 

abstract class ClassToTest 
{ 
    private $someProperty; 
    abstract function someFunc(); 
} 

class TestingException extends Exception 
{ 
} 

編輯:2016年8月31日下午4時32分EST 響應answer by Katie更新後的代碼。

+0

你爲什麼要測試一個抽象類? – dm03514

+0

這樣我就不必爲擴展它們的類編寫額外的測試用例。 – Person93

+1

您仍然必須爲擴展抽象類的類編寫測試用例。它們實現基類的抽象函數,並且沒有辦法通過測試基類來測試這些函數。 – axiac

回答

2

您試圖調用一個嘲笑對象的思考方法,相反,你可以調用它的抽象類本身:

因此改變:

$reflection = new ReflectionClass(get_class($object)); 

$reflection = new ReflectionClass(ClassToTest::class); 

這將適用於任何不是類中抽象的東西,比如你的財產,或者其他完全實現的方法。

附加註釋,因爲OP進行了更新

的修復仍將在getReflectionProperty你的第一線工作。但是,如果您無法訪問課程名稱,那就是個問題。

+0

這確實有效,但這是我使用的實際代碼的簡化版本。在真實情況下,類名不可用。我更新了OP。 – Person93

+0

我有一個方法,它接受一個對象,一個屬性,一個名稱和一個值並相應地設置對象的屬性。我不想更改方法簽名。 – Person93

+0

您需要類名來對類進行適當的反射,從PHPUnit的產生這個錯誤:無法設置對象類型屬性someProperty Mock_ClassToTest_27fdf591:屬性someProperty不存在。對象類型與您的類不一樣。 – Katie

2

使用反射訪問受保護和私有屬性以及測試中類的方法似乎是一種非常聰明的方法,但它會導致難以閱讀和理解的測試。

另一方面,只有一個類的公共接口應該被測試。測試(甚至關心)被測試類的受保護和私有屬性和方法是一個標誌,表明測試是在代碼之後編寫的。這種測試很脆弱;測試類實現的任何變化都會中斷測試,即使它沒有破壞類的功能。

通常不需要測試抽象類。大多數時候,其子類的測試也覆蓋了抽象類的相關代碼。如果他們不覆蓋其中的一部分,那麼無論那個代碼在那裏都不需要,或者測試用例不能涵蓋所有的角落案例。

但是,有時需要爲抽象類編寫測試用例。在我看來,最好的方法是將抽象類擴展到包含測試用例的文件底部,爲其所有抽象方法提供簡單的實現,並將該類用作SUT

東西沿着這些路線:

class sampleTestClass extends PHPUnit_Framework_TestCase 
{ 
    public function testSomething() 
    { 
     $object = new ConcreteImplementation(); 
     $result = $object->method1(); 
     self::assertTrue($result); 
    } 
} 

class ConcreteImplementation extends AbstractClassToTest 
{ 
    public function someFunc() 
    { 
     // provide the minimum implementation that makes it work 
    } 
} 

您正在測試您發佈的代碼進行模擬。嘲笑不意味着被測試。他們的目的是模擬不適合在測試中實例化的SUT合作者的行爲。

爲什麼合作者類嘲笑在測試包括但不限於的原因:

  • 難以建立;例如,當模擬類的構造函數需要許多參數或其他對象時;
  • 協作者是一個抽象類或接口;當測試和測試類被寫入時,實際的實現可能不存在;
  • 協作者的代碼需要花費很多時間來完成或需要額外的資源(磁盤空間,數據庫連接,Internet連接等);
  • 合作者的代碼有永久的副作用;這通常與前面的原因結合在一起。
相關問題