モックは、実際のオブジェクトの動作をテストすることを目的としています。
依存関係をシミュレートするため、単体テストの速度が大幅に低下する可能性がある外部リソースを呼び出す必要はありません。
期待を定義し、それを検証できます。
たとえば、メソッドが特定の回数呼び出されたり、特定のパラメータを使用して呼び出されたりするようにすることができます。
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 は、モックがスタブではない理由を説明する素晴らしい投稿を書きました。
これらを使用できる具体的な状況を見てみましょう:
モックがより意味のあるテストケースをいくつか示します:
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); } }
スタブがより意味のあるテストケースをいくつか示します:
一言で言えば、スタブは実際のオブジェクトの動作をチェックすることを目的としたものではなく、状態をチェックすることを目的としています。
単体テストの主な目的は、各単体/コンポーネントが期待どおりに動作することを確認することですが、実際のコードに加えてそれらのテストも保守する必要があります。
スタブはテストのセットアップを簡素化し、メソッド呼び出しや対話を追跡する必要がない単純なシナリオでは非常に効率的です。
一部のテストに焦点を当てておくことで、不必要な複雑さを防ぐことができます。
モックはメソッド呼び出しとそのパラメータを追跡できます。
実際の動作を表す値を返すことを忘れないでください。そうしないと、誤った安心感を抱いてしまう可能性があります。
メンテナンスに不必要な複雑さを避けるために、モックは控えめに使用する必要があります。
以上がPHP: 嘲笑すべきですか、それともやめるべきですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。