Laravelで，Illuminate\Database\Eloquent\Modelクラスのメソッドをモックする

## 環境情報
https://github.com/tarohida/laravel-models-mocking-example#enviroment

```
$ 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メソッドを生やすことがあります．
このテスト設計が果たして正しいのかについては模索中ですが，ひとまず呼び出し元の関数をテストしないといけないのでモックすることにしました．
しかし，これが凄まじく理解し難く，そもそも実装するにたどり着くまでが非常に難しかったので，ここに共有しようと思います．

## とりあえず動かす

https://github.com/tarohida/laravel-models-mocking-example

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コマンドで生成してから変更を加えていません．

```app/Models/ExampleModel.php

<?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クラスとしました


```app/Models/ExampleModelUse.php 
<?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に必要な箇所がよりわかりやすいかと思います．

```tests/Unit/ExampleTest.php
<?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);
    }
}
```

## 参考元記事

https://stackoverflow.com/questions/26304009/mocking-an-eloquent-model

この記事が全てです．リポジトリ
この記事の内容を手元のコードに取り込んで，無事モックが動作しました．

動いたコードを新しいプロジェクトに切り出して作成したのが，今回のGitリポジトリです．

[^1]: 果たして現れるのか？？？