首頁 > 後端開發 > php教程 > PHP:我該嘲笑還是該走?

PHP:我該嘲笑還是該走?

Barbara Streisand
發布: 2024-12-11 10:36:12
原創
819 人瀏覽過

PHP: Should I mock or should I go?

簡而言之模擬

模擬旨在測試真實物件的行為

它們模擬依賴關係,因此您不必呼叫可能顯著減慢單元測試速度的外部資源。

您可以定義期望並驗證它們。

例如,您可以確保某個方法被呼叫特定次數和/或使用某些參數:

use PHPUnit\Framework\TestCase;

class MyTest extends TestCase
{
    public function testMockExample(): void
    {
        $depencencyMock = $this->createMock(MyDependency::class);

        $dependencyMock->expects($this->exactly(2))
              ->method('someMethod')
              ->with('some parameter');

        $classToTest = new ClassToTest($dependencyMock);
   }
}
登入後複製
登入後複製

傳回值

willReturn() 確保與回傳類型的相容性:

// In code
class MyClass {
    public function getNum(): int {
    }
}

// In tests
$myClassMock = $this->createMock(MyClass::class);
$myClassMock->expects($this->once())
            ->method('getNum')
            ->willReturn(2);
登入後複製

如果你想根據輸入參數來測試動態行為,也可以使用 willReturnCallback。

應避免的不良做法

由於模擬僅模仿真實行為,因此很容易錯過重點。讓我們討論一下常見的不良做法:

無期望回傳值

❌不要這樣做:

$colorServiceMock = $this->createMock(ColorService::class);
$colorServiceMock->method('hexToName')
     ->willReturn('red');

$color = (new MyClass($colorServiceMock))->getColorName('ff0000');
登入後複製

✅ 相反,加入一些期望:

$colorServiceMock->expects($this->once())
     ->method('hexToName')
     ->with('00f00')
     ->willReturn('green');

$color = (new MyClass($colorServiceMock))->getColorName('00f00');
登入後複製

記住模擬的目的是驗證交互作用。

模擬真實物件而不是接口

讓我們來測試一下實作 SomeInterface 的 MyClass。

❌不要這樣做:

$myclassMock = $this->createMock(MyClass::class);
登入後複製

✅ 相反,模擬介面:

$myclassMock = $this->createMock(SomeInterface::class);
登入後複製

模擬關注行為。介面通常不會改變,因為您應該修改實現,而不是契約。

過度模擬測試

Tomas Votruba 精美地解釋了這個問題:從過度模擬測試中提取價值的 5 種方法

使用模擬來掩蓋不良的設計實踐

很容易忽略組件之間的緊密耦合:

$productRepositoryMock = $this->createMock(ProductRepository::class);
$invoiceRepositoryMock = $this->createMock(InvoiceRepository::class);
$emailServiceMock = $this->createMock(EmailService::class);

$overComplexService = new OverComplexService($productRepositoryMock, $invoiceRepositoryMock, $emailServiceMock);
登入後複製

上面的範例打破了關注點分離,而模擬則延續了這種不良做法。

完全依賴模擬

模擬是強大的工具,但單元測試還不夠。您需要各種其他類型的測試(例如整合、e2e)。

如何發現模擬的錯誤使用

除了不良做法之外,還有其他跡象可能表明項目中誤用或過度使用了模擬:

  • 測試不反映真實場景,忽略了生產中的關鍵問題
  • 測試和實現之間存在緊密的聯繫,導致相關模擬的頻繁更新
  • 測驗過於複雜,使得它們更難閱讀和維護

模擬和存根

Martin Fowler 寫了一篇精彩的文章,解釋了為什麼 Mock 不是 Stub。

讓我們看看您可能會使用它們的具體情況:

何時使用模擬

這裡有一些測試案例,其中模擬更有意義:

  • 您需要測試您的類別如何與其依賴項互動
  • 您需要檢查複雜的序列,其中使用不同的參數多次呼叫特定方法

何時使用存根

您可以非常方便地使用 PHPUnit 建立存根:

use PHPUnit\Framework\TestCase;

class MyTest extends TestCase
{
    public function testMockExample(): void
    {
        $depencencyMock = $this->createMock(MyDependency::class);

        $dependencyMock->expects($this->exactly(2))
              ->method('someMethod')
              ->with('some parameter');

        $classToTest = new ClassToTest($dependencyMock);
   }
}
登入後複製
登入後複製

以下是一些測試案例,其中存根更有意義:

  • 您想要測試程式碼的輸出或狀態,而不需要驗證互動
  • 您需要測試一些計算,而不需要與實際資料庫互動

簡而言之,存根並不是為了檢視真實物件的行為,而是狀態

微調

單元測試的主要目的是確保每個單元/組件按預期工作,但除了實際程式碼之外,您還必須維護這些測試。

存根可以簡化測試設置,對於不需要追蹤方法呼叫和互動的簡單場景非常有效。

它可以透過集中某些測試來防止不必要的複雜性。

包起來

Mock 可以追蹤方法呼叫及其參數。

不要忘記回傳代表真實行為的值。否則,你可能會產生一種錯誤的安全感。

應謹慎使用模擬,以避免不必要的維護複雜性。

以上是PHP:我該嘲笑還是該走?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

來源:dev.to
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
作者最新文章
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板