テスト駆動開発(TDD)は、テストを先に書き、そのテストを満たすようにコードを実装していく開発手法です。
PHPにおいても、モックを活用することでTDDを効果的に実践できます。
ここでは、ユーザー情報を扱うシンプルなアプリケーションを例に、モックを用いたテスト駆動開発の流れを解説します。
ユーザー情報を扱うアプリケーション
まず、ユーザー情報を扱うアプリケーションの主要なコンポーネントを見ていきましょう。
User.php
<?php namespace App; class User { private $id; private $name; private $email; public function __construct($id, $name, $email) { $this->id = $id; $this->name = $name; $this->email = $email; } public function getId() { return $this->id; } public function getName() { return $this->name; } public function getEmail() { return $this->email; } }
Userクラスは、ユーザーを表すシンプルなエンティティです。
ユーザーID、名前、メールアドレスをプロパティとして持ちます。
UserRepositoryInterface.php
<?php namespace App; interface UserRepositoryInterface { public function findById($id): ?User; }
UserRepositoryInterfaceは、ユーザーリポジトリのインターフェースです。
findByIdメソッドを定義し、IDによるユーザーの検索を抽象化しています。
UserService.php
<?php namespace App; class UserService { private $userRepository; public function __construct(UserRepositoryInterface $userRepository) { $this->userRepository = $userRepository; } public function getUserName($userId) { $user = $this->userRepository->findById($userId); if ($user) { return $user->getName(); } return null; } }
UserServiceは、ユーザーに関連するビジネスロジックを実装するクラスです。
UserRepositoryInterfaceに依存し、ユーザーIDからユーザー名を取得するgetUserNameメソッドを提供します。
名前空間(namespace App)についてよくわからない場合は、次の記事をご覧ください。
モックを用いたテスト
次に、モックを用いてUserServiceのテストを書いてみましょう。
モックを利用するには、PHPUnitのインストールが前提となります。
UserServiceTest.php
<?php use PHPUnit\Framework\TestCase; use App\User; use App\UserRepositoryInterface; use App\UserService; class UserServiceTest extends TestCase { public function testGetUserName() { // UserRepositoryのモックを作成 $userRepositoryMock = $this->createMock(UserRepositoryInterface::class); // findByIdメソッドの期待値を設定 $userRepositoryMock->expects($this->once()) ->method('findById') ->with(1) ->willReturn(new User(1, 'John Doe', 'john@example.com')); // モックを注入してUserServiceのインスタンスを作成 $userService = new UserService($userRepositoryMock); // テスト対象のメソッドを呼び出す $userName = $userService->getUserName(1); // 期待する結果とのアサーション $this->assertEquals('John Doe', $userName); } }
UserServiceTestは、UserServiceのテストクラスです。
以下の手順でテストを実装します。
- UserRepositoryInterfaceのモックを作成します。
- findByIdメソッドの期待値を設定します。モックがfindById(1)を呼び出されたら、事前に定義したユーザーを返すように設定します。
- モックをUserServiceのコンストラクタに注入して、テスト対象のインスタンスを作成します。
- getUserNameメソッドを呼び出し、期待する結果とのアサーションを行います。
このテストでは、実際のUserRepositoryInterfaceの実装クラスを使用していません。
その代わりに、モックを使ってユーザーリポジトリの振る舞いをシミュレートしています。
モックの振る舞いを定義しているため、実装クラスの内容はテストの結果に影響を与えません。
つまり、実際の実装クラスがどのように動作するかに関係ないということです。
あくまで、モックが期待通りに動くかどうかだけを判定していると言えます。
そのことは、常に意識しておきましょう。
テスト駆動開発の流れ
モックを用いたテスト駆動開発の一般的な流れは以下の通りです。
- インターフェースとテストを先に書く
- テストを満たすように実装クラスを書く
- テストを実行し、すべて成功することを確認する
- リファクタリングを行う
- テストを再実行し、すべて成功することを確認する
この流れを繰り返すことで、信頼性の高いコードを効率的に開発することができます。
まとめ
PHPにおいてモックを活用することで、テスト駆動開発を効果的に実践できます。
モックを使ってテストを書くことで、実際のデータベースや外部サービスに依存せずにテストが可能になります。
そのため、テストの実行速度が向上し、テストの信頼性も高まります。
また、モックを使うことで、テスト対象のコードが依存するオブジェクトの振る舞いを細かく制御できます。
これにより、様々なシナリオでのテストが可能になります。
ただし、モックはテストを容易にするために使用されます。
実際のアプリケーションでは、モックの代わりにUserRepositoryInterfaceの実装クラスが使用されることを忘れないでください。
今回紹介した例を参考に、モックを活用したテスト駆動開発を実践してみてください。
コードの品質向上とともに、開発の生産性も向上するはずです。