

現在Laravelでの開発でPHP UNITテストを行っております。

ServiceクラスのメソッドでCarbonクラスをインスタンス化して、RepositoryクラスのメソッドにCarbonインスタンスオブジェクトを渡している処理があるのですが、その処理で渡しているCarbonクラスの値をMockeryを使ってモッキングしようとしているのですが、エラーが出てうまくモッキングできません。

CarbonクラスはDIしたほうがいいのでしょうか？

[code]

    class HogeServiceImp implements HogeServiceInterface
    {
        private $fugaRepository;
    
        public function __constract(FugaRepository $fugaRepository)
        {
            $this->fugaRepository= $fugaRepository;
        }
    
        public function hogeMethod()
        {
            $now = new Carbon::now();
            $this->fugaRepositroy->fugaMethod($now);
        }
    }
    
[/code]

[code]

    public function test_hogeMethod動作確認()
    {
        $dateTime = Carbon::now();
        $carbonMock = Mockery::mock('overload:' . Carbon::class);
        $carbonMock->shouldReceive('now')->andReturn($dateTime);
    
        $repositoryMock = Mockery::mock(FugaRepository::class);
        $repositoryMock->shouldReceive('fugaMethod')-with($dateTime);
    
        app()->instance(FugaRepository::class, $repositoryMock);
        $hogeService = app()->make(HogeService::class);
        $hogeService->hogeMethod();
    }
    
[/code]


CC BY-SA 4.0
2023-04-27T13:46:34.153
94665
2023-07-04T16:25:18.323
44197
question
php laravel phpunit mock
CarbonクラスをPHP UNIT TEST時にMockeryを使ってモッキングできない


HogeServiceImp の方での Carbon の使い方は問題ないです．  
Carbon は，インスタンスを new
していくつも作ることが当たり前のものですし，インスタンスそれぞれが固有の値（日付の情報）を持つような性質のクラスなので， コンストラクタやセッターから DI
するようなものではありません．どちらかというと「依存するクラス」というより「（感覚としてスカラーに近い）値」と考えると分かりやすいかもしれません．

一方で，基本的にモックしたいようなものは DI し，ふつうにモックを注入できるようにした方が良いです．したがってオートロード時に無理やり行う
overload モックはできるだけ使わず，これは最終手段だと思ってください．

ここまでを踏まえ，今回どのように対処できるかというと， `Carbon::setTestNow()` をテストクラス側で使用し，Carbon が now()
で返す時間をテスト側からコントロールすると良いと思います．

[code]

    public function test_hogeMethod動作確認()
    {
        // Carbon::now() は，これ以降 2023-01-01 00:00:00 を返すようになります
        Carbon::setTestNow('2023-01-01 00:00:00');
    
        $repositoryMock = Mockery::mock(FugaRepository::class);
        $repositoryMock
            ->shouldReceive('fugaMethod')
            -with(equalTo(Carbon::parse('2023-01-01 00:00:00')));
    
        app()->instance(FugaRepository::class, $repositoryMock);
        $hogeService = app()->make(HogeService::class);
        $hogeService->hogeMethod();
    }
    
[/code]

上記のように `Carbon::setTestNow('2023-01-01 00:00:00')` で時計を固定してあげることで，
`HogeServiceImp::hogeMethod()` 内での `Carbon::now()` は 2023-01-01 00:00:00
な状態を持つ Carbon インスタンスを返すようになります．

また，ここで使っている `equalTo()` は，オブジェクトを曖昧比較（＝＝＝ ではなく ＝＝）するためのマッチャーです．  
これを使うことで， FugaRepository::fugaMethod() が引数に Carbon::parse('2023-01-01
00:00:00') を受け取ることを期待できます．  
参考： <https://readouble.com/mockery/1.0/ja/argument_validation.html>


CC BY-SA 4.0
2023-07-04T16:25:18.323
95467
2023-07-04T16:25:18.323
58973
94665
answer