訪問私有和受保護的成員變數
反射通常用作軟體測試的一部分,例如用於模擬物件的執行時建立/例項化。它對於在任何給定時間點檢查物件的狀態也很有用。下面是在單元測試中使用 Reflection 來驗證受保護的類成員是否包含期望值的示例。
下面是一個非常基本的汽車類。它有一個受保護的成員變數,它將包含表示汽車顏色的值。因為成員變數是受保護的,所以我們無法直接訪問它,並且必須使用 getter 和 setter 方法分別檢索和設定其值。
class Car
{
protected $color
public function setColor($color)
{
$this->color = $color;
}
public function getColor($color)
{
return $this->color;
}
}
為了測試這一點,許多開發人員將建立一個 Car 物件,使用 Car::setColor()
設定汽車的顏色,使用 Car::getColor()
檢索顏色,並將該值與他們設定的顏色進行比較:
/**
* @test
* @covers \Car::setColor
*/
public function testSetColor()
{
$color = 'Red';
$car = new \Car();
$car->setColor($color);
$getColor = $car->getColor();
$this->assertEquals($color, $reflectionColor);
}
從表面上看,這似乎沒問題。畢竟,所有 Car::getColor()
都會返回受保護成員變數 Car::$color
的值。但是這個測試有兩個方面存在缺陷:
- 它練習
Car::getColor()
,這超出了本次測試的範圍 - 它取決於
Car::getColor()
,它本身可能有一個錯誤,可以使測試產生假陽性或陰性
讓我們看一下為什麼我們不應該在單元測試中使用 Car::getColor()
,而應該使用 Reflection。假設開發人員被分配了一項任務,即為每種車型新增金屬。所以他們試圖修改 Car::getColor()
以將 Metallic
新增到汽車的顏色中:
class Car
{
protected $color
public function setColor($color)
{
$this->color = $color;
}
public function getColor($color)
{
return "Metallic "; $this->color;
}
}
你看到錯誤嗎?開發人員使用分號而不是連線運算子來嘗試將 Metallic
新增到汽車的顏色中。因此,每當呼叫 Car::getColor()
時,無論汽車的實際顏色是什麼,都將返回金屬。因此,我們的 Car::setColor()
單元測試將失敗,即使 Car::setColor()
工作完全正常並且不受此變化的影響。
那麼我們如何驗證 Car::$color
是否包含我們通過 Car::setColor()
設定的值?我們可以使用 Refelection 直接檢查受保護的成員變數。那麼我們怎麼辦那?我們可以使用 Refelection 使受保護的成員變數可以訪問我們的程式碼,以便它可以檢索該值。
讓我們先看看程式碼,然後將其分解:
/**
* @test
* @covers \Car::setColor
*/
public function testSetColor()
{
$color = 'Red';
$car = new \Car();
$car->setColor($color);
$reflectionOfCar = new \ReflectionObject($car);
$protectedColor = $reflectionOfForm->getProperty('color');
$protectedColor->setAccessible(true);
$reflectionColor = $protectedColor->getValue($car);
$this->assertEquals($color, $reflectionColor);
}
以下是我們如何使用 Reflection 在上面的程式碼中獲取 Car::$color
的值:
- 我們建立一個表示 Car 物件的新 ReflectionObject
- 我們得到了
Car::$color
的 ReflectionProperty (這個代表Car::$color
變數) - 我們讓
Car::$color
可以訪問 - 我們得到了
Car::$color
的價值
正如你可以通過使用 Reflection 看到的那樣,我們可以獲得 Car::$color
的值,而無需呼叫 Car::getColor()
或任何其他可能導致無效測試結果的訪問器函式。現在我們對 Car::setColor()
的單元測試是安全和準確的。