なずなログ

ただのSIer系SEが思ったことや色々書く感じのアレです

【Laravel】自作Artisanコマンドがコマンドラインからは正常に動くのにテストコードでInvalidArgumentExceptionが発生したときの話

Qiitaからの転載。

事象

  • Artisanコマンドを作成した
  • コマンドラインからは正常に動くのを確認
  • よーしテストコードも書いちゃうぞ~っ
  • テストの時だけ何故かエラーが発生する…

環境

$ php --version
PHP 7.3.0 (cli) (built: Dec 21 2018 01:48:02) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.3.0-dev, Copyright (c) 1998-2018 Zend Technologies
    with Zend OPcache v7.3.0, Copyright (c) 1999-2018, by Zend Technologies
    with Xdebug v2.7.2, Copyright (c) 2002-2019, by Derick Rethans

$ php artisan --version
Laravel Framework 5.7.28

Artisanコマンド

<?php

namespace App\Console\Commands;

use App\User;
use App\DripEmailer;
use Illuminate\Console\Command;

class SendEmails extends Command
{
    /**
     * コンソールコマンドの名前と引数、オプション
     *
     * @var string
     */
    protected $signature = 'email:send {user?}';

    /**
     * コンソールコマンドの説明
     *
     * @var string
     */
    protected $description = 'Send drip e-mails to a user';

    /**
     * 新しいコマンドインスタンスの生成
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * コンソールコマンドの実行
     *
     * @param  \App\DripEmailer  $drip
     * @return mixed
     */
    public function handle(DripEmailer $drip)
    {
        $drip->send(User::find($this->argument('user')));
    }
}

テストコード

<?php

namespace Tests\Unit\app\Console\Commands;

use Tests\TestCase;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Foundation\Testing\RefreshDatabase;

class SendEmailsTest extends TestCase
{
    public function setUp()
    {
        parent::setUp();
        // 何らかのセットアップ
    }

   /**
     * @test
     */
    public function 引数なしの場合に正常に終了すること()
    {
        $this->artisan('email:send')
             ->assertExitCode(0);
    }

   /**
     * @test
     */
    public function 引数ありの場合に正常に終了すること()
    {
        $this->artisan('email:send', ['user' => 'hogehoge'])  // ← ここでエラーが発生
             ->assertExitCode(0);
    }
}

発生したエラー

Fatal error: Uncaught Symfony\Component\Console\Exception\InvalidArgumentException: The "user" argument does not exist. in /project/vendor/symfony/console/Input/ArrayInput.php

原因

今回のケースでは、なんらかのキャッシュが残っておりargumentsが古い状態で保有されていたため、「"user"なんて引数ないよー!」って怒られている。 いろいろ試行錯誤してる最中に試しに実行したりすると起きる可能性がある。

protected $signature = $signature = 'email:send {user}';
protected $signature = $signature = 'email:send {user?}';
protected $signature = $signature = 'email:send';

解決策

キャッシュを削除することで解決する。

$ php artisan optimize:clear
Compiled views cleared!
Application cache cleared!
Route cache cleared!
Configuration cache cleared!
Compiled services and packages files removed!
Caches cleared successfully!

最後に

キャッシュの可能性をすっかり忘れていて、無駄に4時間くらい時間を潰してしまった…。 原因を探しているときにキューワーカー(queue:work)で同じエラーに遭遇している人が見つけて、「ああね、変更が反映されないやつね」と思ったが、ふと「あれ、キャッシュのせいで変更が反映されてないってのもあるのでは?」と思ったらビンゴだった。

Possible bug Artisan::queue() with queue:work "The option does not exist" · Issue #17487 · laravel/framework

開発環境では反映されないときに問答無用でまずはキャッシュを消すくらいの感じがいいのかもしれない。