スキルアップ

【Mock】Laravel × PHPUnitでテスト最適化

PHPUnitとLaravelを組み合わせて使用する際、Mockオブジェクトの活用はテストの品質と効率を大きく向上させることができます。この記事では、Mockの基本的な概念から具体的な使い方、そして積極的な使用が推奨されるシナリオについて掘り下げていきます。

Mockとは

Mockとは

Mockオブジェクトは、単体テストで使用される技法の一つです。これは、実際のオブジェクトの振る舞いを模倣するために使用されます。特に外部システムとの連携部分(例えば、データベースや外部API)をテストする際に役立ちます。Mockを使用することで、テストの実行速度を向上させ、外部依存性を排除し、テスト対象のコードの振る舞いをより正確に検証できます。

Mockの使い方

LaravelとPHPUnitを使ったMockの例を考えてみましょう。例えば、外部APIからデータを取得するサービスクラスがあるとします。この外部APIの実際の呼び出しを行わずにテストを行うために、Mockオブジェクトを作成し期待される返却値を定義します。PHPUnitではgetMockBuilder()メソッドを使用してMockオブジェクトを生成し、method()willReturn()をチェーンさせることで、特定のメソッドの戻り値を設定できます。

具体例

namespace App\Services;

class WeatherService
{
    public function getCurrentWeather($city)
    {
        // 外部の天気予報APIにリクエストを送る
        // この例では具体的なAPIの実装は省略します
    }
}
namespace Tests\Unit;

use PHPUnit\Framework\TestCase;
use App\Services\WeatherService;
use App\Clients\WeatherClient; // 仮定する外部APIクライアント

class WeatherServiceTest extends TestCase
{
    public function testGetCurrentWeather()
    {
        // WeatherClientのMockを作成
        $mock_weather_client = $this->createMock(WeatherClient::class);

        // getCurrentWeatherが呼ばれた際に返す予定のレスポンスを設定
        $mock_weather_client->method('getCurrentWeather')
                          ->willReturn(['temperature' => 23, 'condition' => 'Sunny']);

        // WeatherServiceのインスタンスを作成し、依存するWeatherClientにMockを注入
        $weather_service = new WeatherService($mock_weather_client);

        // WeatherServiceのgetCurrentWeatherメソッドを実行し、結果を検証
        $weather = $weather_service->getCurrentWeather('Tokyo');
        
        $this->assertEquals(23, $weather['temperature']);
        $this->assertEquals('Sunny', $weather['condition']);
    }
}

Mockは積極的に使用すべきか

Mockの使用は、テストの独立性と速度を重視する場合に特に有効です。外部依存性が高いコードや、繰り返し同じ外部リソースにアクセスするコードのテストでは、Mockを積極的に利用することが推奨されます。しかし、Mockを過度に使用することで実際の振る舞いから乖離するリスクもあるため、使用する場面を適切に選ぶ必要があります。

Mockを使う場合の危険性

実際の結合の欠如

Mockオブジェクトを多用すると、システム内の実際の結合や相互作用がテストされないことがあります。つまり、個別のコンポーネントは期待通りに動作するかもしれませんが、これらが組み合わさったときの動作は保証されません。これは、システムが実際に稼働した際に未知の問題やバグにつながる可能性があることを意味します。

柔軟性の欠如

Mockを使って詳細に振る舞いを定義することで、テストはその特定の振る舞いに非常に密接に結びつきます。これは、実際のコードに小さな変更があった場合でも、多くのテストが失敗する原因となることがあります。その結果、コードのリファクタリングや拡張が困難になる可能性があります。

実装に依存しないテストの作成

Mockを使ったテストは実装の詳細に依存しないことが多いですが、これは実際の実装がテストでカバーされていないことを意味する場合があります。テストはインターフェイスの仕様を満たしているかもしれませんが、実際の実装が正しいかどうかは別問題です。

こういう場面ではMockを使おう

Mockを最も効果的に活用できるシナリオは以下のような場合です。

  1. 外部APIとの連携テスト:
    外部APIのレスポンスを模倣することで、APIの実際の動作に依存せずにテストが可能になります。
  2. 高コストの操作を含むテスト:
    データベースへの大量の書き込みや読み出しなど、時間がかかる操作をMockで置き換えることで、テストの実行時間を短縮できます。
  3. 不確定な要素を含むテスト:
    ネットワーク状況や外部サービスの可用性に左右されるテストは、Mockを使用して安定性を確保します。

最後に

Mockオブジェクトを適切に利用することで、PHPUnitとLaravelを使ったテストはより効率的かつ信頼性の高いものになります。Mockの使用は、テストの精度を向上させるだけでなく、開発プロセス全体のスピードアップにも寄与します。

フリーランスフルリモート環境に興味がある方は、下記の記事も参考になるかと思います。

  • この記事を書いた人

zuu

現役フリーランスlaravelエンジニアのスキルスタックブログです。 日々の技術的発見やフリーランスエンジニア業界についてのあれこれをご紹介します。エージェントは PE-BANK を利用しています。

-スキルアップ