(社内LT資料)ユニットテスト:ちょっとしたDI
週1回、朝会後に簡単なLTをやることにした。
(残りの4日間は広告業界・アドテク分野のニュースを適当にひっぱってみんなで眺める会)
ひとつのテーマで2〜3人がしゃべる形式。
記念すべき第1回のテーマは「ユニットテスト」。
あえて少し外して、むかし先輩に教えてもらったDIと絡めて話してみた。
併せて見せたサンプルコードは下に。
(普段ユニットテストはPHPUnit使って書いてるけど今回はライブラリにフォーカスしたくなかったので自前で)
サンプルコード
悪い例
改修前
<?php // コントローラ $team = new Team(); echo $team->doAsakai(); // 今日も一日がんばりましょう // モデル class Team { private $leader; public function __construct() { // リーダーは事業マネージャー $this->leader = new ServiceManager(); } public function doAsakai() { return 'リーダーより:' . $this->leader->sayAsakaiMessage(); } } class ServiceManager { public function sayAsakaiMessage() { return '今日も一日がんばりましょう'; } } // テスト class TeamTest { public function execute() { $team = new Team(); echo 'doAsakai()のテスト'; if ($team->doAsakai() === 'リーダーより:今日も一日がんばりましょう') { echo 'OK!!'; } else { echo 'NG!!'; } } }
改修後
<?php // コントローラ $team = new Team(); echo $team->doAsakai(); // にゃーん // モデル class Team { private $leader; public function __construct() { // リーダーはエンジニアマネージャー $this->leader = new EngineerManager(); // <- 改修 } public function doAsakai() { return 'リーダーより:' . $this->leader->sayAsakaiMessage(); // <- 出力が変わった } } class ServiceManager { public function sayAsakaiMessage() { return '今日も一日がんばりましょう'; } } class EngineerManager { public function sayAsakaiMessage() { return 'にゃーん'; } } // テスト class TeamTest { public function execute() { $team = new Team(); echo 'doAsakai()のテスト'; if ($team->doAsakai() === 'リーダーより:にゃーん') { // <- テストも変える echo 'OK!!'; } else { echo 'NG!!'; } } }
すこし良い例
改修前
<?php // コントローラ $leader = new ServiceManager(); // リーダーは事業マネージャー $team = new Team($leader); echo $team->doAsakai(); // モデル class Team { private $leader; public function __construct($leader) { // リーダーは誰でもいい $this->leader = $leader; } public function doAsakai() { return 'リーダーより:' . $this->leader->sayAsakaiMessage(); // leader の sayAsakaiMessage() と同じ挙動であればOK } } class ServiceManager { public function sayAsakaiMessage() { return '今日も一日がんばりましょう'; } } // テスト // sayAsakaiMessage() を持ったモッククラス class ManagerMock { public function sayAsakaiMessage() { return '私は sayAsakaiMessage() メソッドを持っています'; } } class TeamTest { public function execute() { $leader = new ManagerMock(); $team = new Team($leader); echo 'doAsakai()のテスト'; if ($team->doAsakai() === 'リーダーより:私は sayAsakaiMessage() メソッドを持っています') { echo 'OK!!'; } else { echo 'NG!!'; } } }
改修後
<?php // コントローラ $leader = new EngineerManager(); // リーダーはエンジニアマネージャー <- 改修はここだけ $team = new Team($leader); echo $team->doAsakai(); // モデル class Team { private $leader; public function __construct($leader) { // リーダーは誰でもいい $this->leader = $leader; } public function doAsakai() { return 'リーダーより:' . $this->leader->sayAsakaiMessage(); } } class ServiceManager { public function sayAsakaiMessage() { return '今日も一日がんばりましょう'; } } class EngineerManager { public function sayAsakaiMessage() { return 'にゃーん'; } } // テスト // sayAsakaiMessage() を持ったモッククラス class ManagerMock { public function sayAsakaiMessage() { return '私は sayAsakaiMessage() メソッドを持っています'; } } class TeamTest { public function execute() { $leader = new ManagerMock(); $team = new Team($leader); echo 'doAsakai()のテスト'; if ($team->doAsakai() === 'リーダーより:私は sayAsakaiMessage() メソッドを持っています') { // <- テストも変わらない echo 'OK!!'; } else { echo 'NG!!'; } } }
良い例
改修前
<?php // コントローラ $leader = new ServiceManager(); // リーダーは事業マネージャー $team = new Team($leader); echo $team->doAsakai(); // インタフェース interface Manager { public function sayAsakaiMessage(); } // モデル class Team { private $leader; public function __construct(Manager $leader) { // <- インタフェースで型宣言 // リーダーは誰でもいい $this->leader = $leader; } public function doAsakai() { return 'リーダーより:' . $this->leader->sayAsakaiMessage(); // Managerインタフェースの実装クラスは必ずsayAsakaiMessage()を持っている } } class ServiceManager implements Manager { // <- Managerインタフェースを実装 public function sayAsakaiMessage() { return '今日も一日がんばりましょう'; } } // テスト // sayAsakaiMessage() を持ったモッククラス class ManagerMock implements Manager { // <- Managerインタフェースを実装 public function sayAsakaiMessage() { return '私は sayAsakaiMessage() メソッドを持っています'; } } class TeamTest { public function execute() { $leader = new ManagerMock(); $team = new Team($leader); echo 'doAsakai()のテスト'; if ($team->doAsakai() === 'リーダーより:私は sayAsakaiMessage() メソッドを持っています') { echo 'OK!!'; } else { echo 'NG!!'; } } }
改修後
<?php // コントローラ $leader = new EngineerManager(); // リーダーはエンジニアマネージャー $team = new Team($leader); echo $team->doAsakai(); // インタフェース interface Manager { public function sayAsakaiMessage(); } // モデル class Team { private $leader; public function __construct(Manager $leader) { // <- インタフェースで型宣言 // リーダーは誰でもいい $this->leader = $leader; } public function doAsakai() { return 'リーダーより:' . $this->leader->sayAsakaiMessage(); // Managerインタフェースの実装クラスは必ずsayAsakaiMessage()を持っている } } class ServiceManager implements Manager { // <- Managerインタフェースを実装 public function sayAsakaiMessage() { return '今日も一日がんばりましょう'; } } class EngineerManager implements Manager { // <- Managerインタフェースを実装 public function sayAsakaiMessage() { return 'にゃーん'; } } // テスト // sayAsakaiMessage() を持ったモッククラス class ManagerMock implements Manager { // <- Managerインタフェースを実装 public function sayAsakaiMessage() { return '私は sayAsakaiMessage() メソッドを持っています'; } } class TeamTest { public function execute() { $leader = new ManagerMock(); $team = new Team($leader); echo 'doAsakai()のテスト'; if ($team->doAsakai() === 'リーダーより:私は sayAsakaiMessage() メソッドを持っています') { echo 'OK!!'; } else { echo 'NG!!'; } } }