環境情報
$ php --version
PHP 7.4.5 (cli) (built: Apr 19 2020 07:36:30) ( NTS )
$ composer info laravel/framework
name : laravel/framework
descrip. : The Laravel Framework.
keywords : framework, laravel
versions : * v8.10.0
Homestead: v10.12.0
OS: Ubuntu18.04 LTS
Illuminate\Database\Eloquent\Modelのテスト
Laravelの Illuminate\Database\Eloquent\Model
を継承したクラスに,publicメソッドを生やすことがあります.
このテスト設計が果たして正しいのかについては模索中ですが,ひとまず呼び出し元の関数をテストしないといけないのでモックすることにしました.
しかし,これが凄まじく理解し難く,そもそも実装するにたどり着くまでが非常に難しかったので,ここに共有しようと思います.
とりあえず動かす
cloneして動かせるサンプルを用意しました.これを動作させると,とりあえずモックのコードが試せます.
$ vendor/bin/phpunit tests/Unit/ExampleTest.php
PHPUnit 9.4.1 by Sebastian Bergmann and contributors.
. 1 / 1 (100%)
---- $mock の中身を出力 ----
/home/vagrant/blog/laravel-models-mocking-example/tests/Unit/ExampleTest.php:29:
class App\Models\ExampleModel#419 (20) {
protected $_mockery_expectations =>
...(略)...
}
-------------------------------
Time: 00:00.096, Memory: 20.00 MB
OK (1 test, 2 assertions)
何を変更したのか
このコミットでの変更内容が全てです.
https://github.com/tarohida/laravel-models-mocking-example/commit/e351eb15ae5187b0c7d895cf87e10d2a0fc6e307
構成ファイル
App\Models\ExampleModel
今回モックする対象のモデルクラスです.artisanコマンドで生成してから変更を加えていません.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class ExampleModel extends Model
{
use HasFactory;
}
App\Models\ExampleModelUse
モック対象のExampleModel
クラスを呼び出すクラスです.今回のテスト対象となるクラスです.
artisanコマンドで生成した後,static関数をひとつ定義しています.
本当は,このクラスはModelクラスにする必要はないですし,Modelクラスでない方が一般的なケースかなと思うのですが,実際に laravel new
から変更内容を再現する利用者1のことを考え,artisanコマンドで簡単に作成できるModelクラスとしました
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class ExampleModelUse extends Model
{
use HasFactory;
public static function method_to_be_tested()
{
return ExampleModel::method_do_not_exists();
}
}
Tests\Unit\ExampleTest
テストケースです.laravelがデフォルトで作成するExampleTestクラスを書き換えています.
上記コミットの変更差分を確認いただければ,Mockに必要な箇所がよりわかりやすいかと思います.
<?php
namespace Tests\Unit;
use Tests\TestCase;
use Mockery;
use App\Models\ExampleModelUse;
class ExampleTest extends TestCase
{
public function setUp(): void
{
parent::setUp();
}
/**
* A basic test example.
*
* @return void
*/
public function testBasicTest()
{
// ExampleModelのmethod_do_not_exists()メソッドをモックする
$mock = Mockery::mock('overload:App\Models\ExampleModel');
$mock->shouldReceive('method_do_not_exists')
->once()->andReturn('mocked_value');
echo "---- \$mock の中身を出力 ----\n";
var_dump($mock);
echo "-------------------------------";
// テスト対象のメソッドを呼び出して,assertする
$example_ret_value = ExampleModelUse::method_to_be_tested();
$this->assertSame('mocked_value', $example_ret_value);
}
}
参考元記事
この記事が全てです.リポジトリ
この記事の内容を手元のコードに取り込んで,無事モックが動作しました.
動いたコードを新しいプロジェクトに切り出して作成したのが,今回のGitリポジトリです.
-
果たして現れるのか??? ↩