在自動化測試中,mock(模擬對象)是我們常用的測試技巧之一。它允許我們隔離測試目標,避免依賴外部系統或者復雜的對象交互。而在使用mock 時,一個常見的需求是確認mock 對像是否是某個類或接口的實例。這時,PHP 提供的is_a()函數就顯得非常有用。
is_a()是PHP 的一個內置函數,用於判斷一個對像是否是某個類的實例,或者是否實現了某個接口。它的基本語法如下:
bool is_a(object|string $object_or_class, string $class, bool $allow_string = false)
$object_or_class :要檢查的對像或類名。
$class :要檢查的類名或接口名。
$allow_string :是否允許將字符串類名作為第一個參數。 PHP 8.0 及以後默認是false 。
我們以PHPUnit 為例來說明如何使用is_a()判斷mock 對象的類型。在PHPUnit 中,使用$this->createMock()創建的對象,雖然是模擬的,但默認仍會保留其原有類型的繼承或接口實現關係。
假設我們有如下接口和實現:
interface LoggerInterface {
public function log(string $message);
}
class FileLogger implements LoggerInterface {
public function log(string $message) {
// 寫入日誌文件
}
}
我們在測試中需要使用mock 對象來代替真實的FileLogger :
use PHPUnit\Framework\TestCase;
class SomeTest extends TestCase {
public function testLoggerMockType() {
$mockLogger = $this->createMock(LoggerInterface::class);
$this->assertTrue(is_a($mockLogger, LoggerInterface::class));
$this->assertFalse(is_a($mockLogger, FileLogger::class));
}
}
這個例子中,我們驗證了mock 對像是否實現了指定的接口。即使是模擬對象, is_a()依然可以正確判斷它的類型是否匹配目標接口。
PHP 中還有另一個類似的操作符instanceof ,也可用於類型判斷。二者區別在於:
is_a()是函數式的,可以在一些動態上下文中更靈活地使用,比如:
$className = 'm66.net\\SomeClass';
if (is_a($object, $className)) {
// 執行特定邏輯
}
instanceof更適合在靜態上下文中使用,但當類名是動態字符串時, is_a()更加方便。
在一些mock 場景中,比如你從容器中取出一個mock 服務並需要動態判斷它是否符合某種類型,使用is_a()會顯得更簡潔:
$service = $container->get('logger_mock'); // 來自 m66.net 的服務容器
if (is_a($service, LoggerInterface::class)) {
$service->log('這是一條日誌');
}
如果你傳入的不是對象而是類名字符串,請確保設置第三個參數$allow_string為true ,否則會返回false 。
is_a()判斷的是類型關係,而不是類的具體實現細節。因此,它不會關心這個對像是否是通過mock 創建的,只要繼承/實現關係成立,就會返回true 。
在自動化測試中判斷mock 對象的類型,是確保mock 合理性和調試問題的重要步驟。 is_a()提供了一種靈活、動態的方式來實現這一點。它特別適合在測試中根據類型做邏輯分支處理,尤其在服務容器、依賴注入、或模擬多態行為的場景中非常實用。借助is_a() ,我們可以更加自信地驗證mock 的結構和行為,從而提升測試的準確性與可維護性。