PHPUnit のモックオブジェクトを使って、想定されたオブジェクトのメソッドが想定された引数を渡されて呼ばれていることをテストします。モックオブジェクトは具体的に何の役に立つのかモヤモヤしてたのですが、動かしてみてやっとメリットが理解できました。
まず、下記はテスト対象となるクラスです。greet メソッドは引数に $name を取り、Hello という文字列とつなげて返却します。
Hoge.php
1 2 3 4 5 6 7 8 |
<?php class Hoge{ function greet($name){ return 'Hello, ' . $name; } } |
次にテスト用のコードです。このコードで Hoge クラスのモックオブジェクトを作成します。
TestHoge.php
1 2 3 4 5 6 7 8 9 10 11 |
<?php class TestHoge extends PHPUnit_Framework_TestCase{ function testGreet(){ $hoge = $this->getMock('Hoge', array('greet')); $hoge->expects($this->once()) ->method('greet') ->with('Saitama'); } } |
5行目
getMock メソッドを使ってモックオブジェクトを作成します。Hoge というクラスの greet メソッドのモックを作成します。
6行目
このモックオブジェクトが1回呼び出されることをテストします。
7行目
呼び出されるのは greet メソッドであることをテストします。
8行目
引数として Saimata という文字列が渡されることをテストします。
ここまででテストコードを実行してみると、下記の実行結果が表示されます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
PHPUnit 3.6.10 by Sebastian Bergmann. F Time: 0 seconds, Memory: 1.75Mb There was 1 failure: 1) TestHoge::testGreet Expectation failed for method name is equal to <string:greet> when invoked 1 time(s). Method was expected to be called 1 times, actually called 0 times. FAILURES! Tests: 1, Assertions: 1, Failures: 1. |
テストは失敗します。これはモックオブジェクトの greet メソッドが 1 回呼び出されることを期待していたのに、実際は 1 度も呼び出されていないからです。なので、このテストを成功させるために、モックオブジェクトの greet メソッドを呼び出すようにしてみます。
TestHoge.php
1 2 3 4 5 6 7 8 9 10 11 12 |
<?php class TestHoge extends PHPUnit_Framework_TestCase{ function testGreet(){ $hoge = $this->getMock('Hoge', array('greet')); $hoge->expects($this->once()) ->method('greet') ->with('Saitama'); $hoge->greet('Saitama'); } } |
9 行目に greet メソッドに Saimata という引数を渡して実行するコードを追記しています。このテストを実行すると、テストは成功します。つまり、6 行目から 8 行目で記述した動作がされていないと、PHPUnit が教えてくれるということです。
ただ、テストコード内に直接 greet メソッドを呼び出すよう自分で書いたのですから成功するのは当たり前ですね。このままでは意味がありません。引数に Hoge オブジェクトを取り、greet メソッドを使っている処理があるクラスをテストする場合に、Hoge オブジェクトの代わりにモックオブジェクトを渡してしまえば、ちゃんと呼び出してくれているのかテストすることができます。この場合はモックオブジェクトを使ってテストをする意味があります。
モックオブジェクトを使うと、目的のオブジェクトの目的のメソッドを呼び忘れていたり、目的のメソッドに誤った引数を渡してしまっていたりすることを検出することができます。